Git Product home page Git Product logo

padlock's Introduction

A Cryptographic ABE Padlock

This is a simplification of the attribute-based-encryption padlock concept. This implementation is just focusing on getting the language right first, and using BLS Elliptic Curves, so that there are proper point hashes.

This means that we should be able to publicly encrypt to a set of attributes without involving the CA. The CA will only be needed to issue certificates.

logic-gates.png

The attributes form a restricted set of logic gates that produce a secret key. If we don't have the proper signed facts, then there isn't enough information to produce any keys. If we try to collude with other users, our shares of the key are incompatible to be added together. This is an extension of public key cryptography in that we only need facts about us attested in certificates, and we can perform key derivations with it offline thereafter.

Smoke Test

./cmd/padlock/testit.sh

Facts

A user makes a request to a CA, asking that it attest to some set of attributes. The CA can refuse to sign it, or to add other attributes.

I am a citizen of the US, and verified not a citizen of Saudi Arabia. This is my email address, and I am an adult.

[
	"citizenship:!SA",
	"citizenship:US",
	"email:[email protected]",
	"age:adult"
]

Users

A different user. Also verified to be not a citizen of China, and a dual citizen of Netherlands and Israel. Also an adult with an email to be attested.

[
	"citizenship:!CN",
	"citizenship:!SA",
	"citizenship:NL",
	"citizenship:IL",
	"email:[email protected]",
	"age:adult"
]

When users submit their public, and non-sensitive certificate requests to a CA, they get back a secret certificate; that could well be inserted into a JWT token. A JWT token would allow it to be used in an http header; provided that the number of facts is not very large. The expiration date would not be cryptographically enforceable, but the tooling could refuse at a different level.

The signer is public. The facts are private, and should not be shared. This json struct is a secret for the user. The facts are signed individually.

{
  "signer": "EmtgKiWirc0BgesmSuFfaF+cvhxjb6jpCNQ0ouKZKGZDgCGgJtPvhwol1d0LHTbGEdi+BxKsa6aliyftVdJ3PLdeJEZWCJ2WrByytzym2CVNc21iR8ZE3pdCKHikkhQRC3FOU/MgSY2ShluKFyJ5y3fJAjwD3BKzupNP+rWpAZ7srM3ybnChUjgw1TK7p4HXGX7DkeUS7jseNJm11x88pKVx9ANBynH7Y4tF5iH9d9wbiWE3ZFsajPKHE17h9e4S",
  "facts": {
    "age:adult": "A35WCknGj0ld9O5NS1dn6BT43xcK0oI+zNOoJ6BXfw7lhMSM2wzJ3nH705NmOm05A5CcHNjH22/6pFLb5kXgWS220oKq3/uyS1GWEcylU610XnmIaMFFIvTo8OnLZuer",
    "citizenship:!SA": "DQ0IKUWtzorMcvn2UZUeQPrk+P/smw1flpYV+GOAc81qqOcBExzE9ht6MT6+vwBKCrWHbYsuwxnBWLX76wAwAHLHQ/QwOKo9CuC491iCukeDF64KLmtwobbBOENpkycc",
    "citizenship:US": "EiQhsVHvU18+IYUeUQb18ZxxQpzLNrYfLvqvfaQm3UZbzSB/Mi5EVBuj3ScsYFBZAw1MxLukZFai9Ak+pR/Shi1ZhZ5aCGjRQ6qJXKucroebimIdNV3YABmo3vB4vzRF",
    "email:[email protected]": "D8Jja59zg7zgE27eMexHEnG5WhQVcXrFgxHoSc828/blmr+L0g1zSXmAbHKp32OIAiKTPZNEi+JSGVAQFPkdeiCMQ3dDRLDix9p+2XWRUu74QjGf81JqeiNwVAOqf6/N"
  }
}

It was created by the CA with:

# padlock command as run by the CA who has a request to make a user
# --priv is a secret for the private key.  it must have enough entropy to be secure (ie: 256-bits)
# --facts are the facts that are requested to be testified to
# The output is the secret cert.  Encoding this as a JWT makes sense. (TODO).
# That would mean that an expiration date should be requested.

padlock \
  --priv farkfark \
	--facts ./requests/alice.request.json \
	> ./users/alice.cert.json

Blueprints and Padlocks

If Alice wants to make a padlock, it can be written to work for her signing domain, the public signer. The padlock's creation need not involve the CA; and it can be created offline.

Given a blueprint for a padlock, a padlock can be created.

padlock \
  --cert ./users/alice.cert.json \
	--blueprint ./blueprints/isAdultCit.blueprint.json \
	> ./padlocks/isAdultCit.padlock.json

Given a blueprint that has been compiled to a padlock, we can generate specific target keys (or random key material). The blueprint is sensitive information, and it should be destroyed after the padlock is created.

We want to generate from a cert and a blueprint. Use random keys that we can use our own certificate to unlock and extract them.

{
	"label": "ADULT",
	"fg": "white",
	"bg": "black",
	"cases": {
		"isOwner": {
			"key": "Write",
			"expr": {
				"and": [
					{"requires": "isAdultCit"},
					{"some": ["email","[email protected]","[email protected]"]}
				]
			}
		},
		"isAdultCit": {
			"key": "Read",
			"expr": {
				"and": [
					{"some": ["citizenship", "US", "NL"]},
					{"some": ["citizenship", "!SA"]},
					{"some": ["age", "adult"]}
				]
			}
		}
	}
}

This padlock means:

  • Rendering hint of a label like ADULT, with a black background, white foreground.
  • A Read key can be generated by meeting isAdultCit criteria.
  • A Write key can be generated by meeting isAdultCit and having one of the emails.
  • "some" means that first arg is fact name, and other args are required value (or logic).
  • "every" is much like some, except every value is required (and logic).

The expr language supports:

  • and, or nested arbitrarily
  • requires is a reference to an existing case (no reference cycles allowed)
  • some for ["citizenship", "US", "IL"] means fact citizenship:US or citizenship:IL is sufficient.
  • every for ["role", "admin", "user"] means that role:admin and role:user are both required.
  • By convention, negated facts are just going to use ! in front of their values. They must be explicitly asserted to work.

When a user is applied to a padlock, keys are yielded.
This is a form of public key secret derivation. Instead of encrypting a file to a user, it can be encrypted to a padlock blueprint. The facts in a user certificate determines if the correct keys are yielded. This is the essence of cpabe (ciphertext policy attribute based encryption).

Unlocks

# alice private Certificate
alice private Certificate
{
  "signer": "EmtgKiWirc0BgesmSuFfaF+cvhxjb6jpCNQ0ouKZKGZDgCGgJtPvhwol1d0LHTbGEdi+BxKsa6aliyftVdJ3PLdeJEZWCJ2WrByytzym2CVNc21iR8ZE3pdCKHikkhQRC3FOU/MgSY2ShluKFyJ5y3fJAjwD3BKzupNP+rWpAZ7srM3ybnChUjgw1TK7p4HXGX7DkeUS7jseNJm11x88pKVx9ANBynH7Y4tF5iH9d9wbiWE3ZFsajPKHE17h9e4S",
  "unwrap": "BEeuLZNkCep0lmj7QNFSxEICRdL72I/qSOAW5KjINvIeSDXJ6BXRgxrMlGnrh3HQGFVcyr3sZabvq2QuE2uYtCM5JSp9QqfKwEN72Ki7DE7CJed7VXM2kbfIluuCYAtDCfzzrIsNVSBa3Kfz6YZGf5MCPUOHMqlxj+LIvbKeCAjlfEkTiUB/e4w+x0dpfRypEOQ7amYhMkqUCYhsMNAprc/fN7H7b2Lbfc76uezxZaNcNc8MVHlE/DD6FeR5Pa+C",
  "facts": {
    "age:adult": "ETk7MIYKsIUmOYjBpMUpPxXB+0lqVOXbFEiqC4esSizy3B791VXZIVSH8yHAKZYfBkRV+rWaZEb/lXPCdDlx/ijhJSYPHeWe2EBhVdiTR2V/RutDcDijqD5Z83CtiZ/J",
    "citizenship:!SA": "Ackq5CPlDgnisEUYzlLnJOMlMXqRH3wOtaVEoW5zyAi6E8uLK78xfiqiOg+gKR12Fg5H4+8CINkKQhYEWpHUswSaz/qMFFN0S9GpkNnCGMcOJ1vx6BNPGv4dnoE/0eC1",
    "citizenship:US": "FXlQmipE26rT+xLCynNS7YT950OUrt7GZc6ufjR+LdgUx8giRVW1aRkqlyFFiUVrDO68+jcaXZHNtKtFDYQxxjn5Cc4l6Bg3OlPA4bykitCwrMvZ1vAmz6M5Kf0n0XMV",
    "email:[email protected]": "D6LqT8Xn//cCNd5Sbc0/2TlgXfJdV2YO4OTiivm4TFBHndH7TuW2RFOTufisTA/ABcQg/xl/HgtlqAICbnUTumMmPSKdjajwUFlB7nYtVf67sb13M2HOxRcVBJkRaHB/"
  }
}
bob private Certificate
{
  "signer": "EmtgKiWirc0BgesmSuFfaF+cvhxjb6jpCNQ0ouKZKGZDgCGgJtPvhwol1d0LHTbGEdi+BxKsa6aliyftVdJ3PLdeJEZWCJ2WrByytzym2CVNc21iR8ZE3pdCKHikkhQRC3FOU/MgSY2ShluKFyJ5y3fJAjwD3BKzupNP+rWpAZ7srM3ybnChUjgw1TK7p4HXGX7DkeUS7jseNJm11x88pKVx9ANBynH7Y4tF5iH9d9wbiWE3ZFsajPKHE17h9e4S",
  "unwrap": "CeUtMj239CCTUCLsggA72Wg1+fZ8Ja5nFJyXW8d/ctYbsMaENN5BprQwMR8qUp9tGAhSgMosMalCi5tVkF2Y6opXgy0dV0FMl6zJRwktp5oV0es0sPH41gCsMfojzzWbAvdN3D8Z3BpjusHO5S9MeWRM8I8ZP7Q8cqJdI8F/yMRhGkKPu7Z09+xDyRkaMMK2DdTV9xPGlZs1LqRgPUi7mhjUyy3bLrYM8+55zj6SA+XK/tiSYM4n2mOHRUYor4I/",
  "facts": {
    "age:adult": "Aqh72ejfBL2lGfo6pj3pbmuVQ225k7WluRps0+Kv6iHm66eqChFg/GzWT7HngmJbBtz5BdYzjCB9esgdTJbFSSdSSL6+pFaqTnGnbzsSsO0712my0PsRDBZANsbgm9lk",
    "citizenship:!CN": "Gdx9Yg4WHX8s5GpoAkoZ6i1o79ylf//IKn5aAHznr+i5KbLh541SeGKI2WbPWh9RBhFlZoJO7odAVO20d2SlBN5jvq+7jaaY0gBWBuaBHdy8bMNEFVNr9NnvTqcdcyfW",
    "citizenship:!SA": "DiTHtrUHpABldkjhRNXAjb2BWUd9W5+IeQ8s0yNBpz5rVzuaXSY7VIYehr1FwfeAFwwUoEnsGfrjaozxWAmuRBwvcD87zjFN+P9bvpSTQo8WlQuQNeyB7xD/Le93sAwk",
    "citizenship:IL": "A4E03f2rLbQ7VW4kGpaG5LUluZY6ITYSc7PjHHNAF2xhYAD2B6lrRpoWYRDxIhq5Ca79ULjrUNwu68EUr1i3GXZ+roFV5MefcvBAGR6LlGX1hdbQjSpP6r9ooToB/Dmy",
    "citizenship:NL": "AaEH5abjP4XvarK9XeocaiIajR+vme61mMwPCNy+PLSddfe85g9dpTcBsvQPCyNDEeYr1taVDq4rju5Jp1+3hf8/FC2OzJXjHwkuzUEgwmmcHbTZCl37P11WQ2J8irFT",
    "email:[email protected]": "DWQe9y/6xzRUp39U1eL9SFJ17SwbjISKAjUWdJxY1uH92nfTlKXQUIWGqS8pRNhnFFJXZYT2qinDl2Dd3UViCoxdkC+3O/ejZXhsjNkpttPH+XEa5y19F8hLRUUhoVn8"
  }
}
alice Unlocks
{
  "Read": "m5qNBafsNTvahPnBuzF4wpneMAG16XBQjdyInEh/kso=",
  "Write": "PwCSenGTRe3UqDFlmdOzKIV5h1R/iIQwaGEWH/oJZH4="
}
bob Unlocks
{
  "Read": "m5qNBafsNTvahPnBuzF4wpneMAG16XBQjdyInEh/kso="
}

Every user that has genuine signed facts for the same signer will generate the same keys during unlock. The unlock output can be used as key material for a decrypt key in the Read case, and in the Write case, it can be used to deterministically generate a signing key (TBD). The point is to be able to put on padlocks that can enforce Read and Write rules with cpabe.

./padlock \
  --cert ./users/alice.cert.json \
	--padlock ./padlocks/isAdultCit.padlock.json

In this case, a set of keys comes back by name.

Mathematical Basis

A pairing function e ends up with the same value when the CA signing secret s and the file secret nonce f are swapped. This is why it is possible to create a padlock without assistance from the CA, and to unlock without assistance from the CA; such that the CA is only involved in attesting to facts about a user.

  • A CA public key secretly signed with s paired with a sum of attributes signed with f can be used to make a padlock.
  • A file public key secretly signed with f paired with a sum of attested attributes signed with s can be used to unlock a padlock with a certificate.

pairing equation

An Elliptic Curve pairing key is used to perform this task. It is extremely important that:

  • The curve has a proper point hash, so that values in the hash need not be secret.
  • Given public hash values, the CA need not be involved in padlock creation.
  • This is similar to Identity Based Encryption. Exception IBE is one-attribute only. IBE allows for encryption to be made to a chosen-text public key; such as an email address. That way, encryption can be made to an email address, under the authority of a CA. And the CA must issue the private key for that email address later. This is the opposite of how public key crypto usually works, where there is no direct control over what the public key is, so that CAs need to explicitly sign public keys. Because of this, we only need to trust that a CA won't issue a certificate with untruthful information in it.

There is now collusion resistance, in that attributes cannot be combined between users. It is however possible to take shortcuts with the file nonce, so that a privileged user can unwrap a file with f = 1, and exponentiate the end result to the power of the publicly known nonce values. But fortunately, this doesn't allow for any privilege escalations.

Implementation Issue

A simplified form of the idea would be to imagine a less secure implementation of the idea:

  • A fact is a secret hash made by the CA. ie: Sha256("citizen:US" + caSecret)
  • So, each attested fact has a secret, and deterministic value.
  • To AND together facts, xor their attested hashes.
  • To OR together facts, calculate exhaustively all possible AND cases.
  • Store a value to xor the case with to produce a target key.
  • A negative fact works just like its positive fact, except something is wrong if both the truth and falsity are attested.
  • Using AND and OR and negated facts, we can produce witness values for them. But in general, it is not possible to produce witnesses on a general NOT function. There would need to be a witness for each possible way of leaving the value unsatisfied.

Example of normal boolean logic, that even works with probabilities as input:

  • a = 0 | a = 1
  • b = 0 | b = 1
  • and(a,b) = (a*b)
  • not(a) = (1 - a)
  • or(a,b) = a + b - a*b

Since we don't need to produce any specific secret witness of these values, there is no problem producing a not. This kind of logic doesn't help us to produce keys, because we need no proof that we know values for a or b.

But if this is done with witnesses (ie: Constructivist Logic):

  • A = Hash("a"+s)
  • B = Hash("b"+s)
  • -A = Hash("!a" + s) // not is explicitly asserted, with no notions of mutual exclusion
  • And(A,B) = (A โŠ• B) // A commutative, associative, idempotent function (ie: Lagrange poly)
  • Or(A,B) = A ~ B // Reduce to A if known, or B if known

For simple cases where the inputs to And are unique, * will suffice, because idempotence is not necessary.

The value A is proof that the signer knew "a" and s. The Or function is a matter of reducing Or(A,B) to a proof of either A or B proof. A Not doesn't have an obvious way of attesting it in general. There may be a way to take the whole user set of facts, and deterministically produce a value that proves that the value is absent. (ie: Produce a number that proves that the fact isn't anywhere in the certificate, in an unforgeable way.) There is the problem of whether values are mutually exclusive, such as citizen:US and citizen:IL.

An attempt to combine boolean logic with proof-relevant logic might go something like this.

  • A = Hash("a" + s)
  • B = Hash("b" + s)
  • And(a:A, b:B) = and(a,b) : And(A,B)
  • Not(a:A) = (1-a) : (N + -A) // this is "not(proof of A)", which does not mean "proof of not A"
  • Or(a:A, b:B) = or(a,b) : Or(A, B)

The problem lies with Not. Calculating a non-forgeable single value for Not of an expression seems difficult. Given a complete environment of known values might allow it to be possible; such as a list of all known facts. Lagrange Polynomial points made of facts might allow this.

Most of the difficulty in this scheme is in translating a convenient language into "and over or" format. The reason to use Elliptic Curves with Parings and Point Hash, is to limit collusion to an individual file at least. Collusion is to take an a issued to one user and a b from a different user, and forming and(a,b) with it. This is an unwanted form of privilege escalation. It is apparently possible to go further and limit collusion even on the same file (however, I cannot convince myself of this fact).

Resist Collusion

A choice has to be made whether to strongly resist collusion between users, or to have a secret file nonce. Collusion between users allows users to combine attributes in a way that escalates privilege. Not keeping the file nonce secret means that it's possible to use one file unlock against all possible nonces. It's not so much a privilege escalation as it is an annoyingly easy way for a user to efficiently leak unlocks. It appears that the best choice is to stop collusion to escalate privilege. A malicious, or careless, user can simply leak his certificate in any case. There isn't any way to avoid trying to put in code to refuse to do things that are cryptographically unenforceable. We should have certificate expirations, and try to make code honor them. And we should be able to honor revocation lists, though they are also not cryptographically enforceable.

resist-collusion.png

Currently, attributes are signed with s, which allows collusing users to add their attributes from their certificates together (the facts), because same facts currently have same value. But signing with s-u, where s is still the global CA secret, and u is a per-user secret nonce used during certificate generation. The value f for a file was previously kept secret during the creation of a file, and only f G2 was published as a public key. But it's a better idea to use (1 + u/(s-u)) G2 as the key to pair to attributes signed with (s-u). This will force the value f to be public though. It's probably the best tradeoff, because as user keys leak; the damage must be contained to individual certificates, rather than the total of all leaked certificates.

Examples:

We are taking advantage of this pairing identity, where H1 is a point hash that is important to have, so that we don't try to do H("fark") * G1 and not realize that this hash is trivially inverted because H("fark") is just a number, and it's easy to compute inverse numbers in this group. Instead, we use a point hash function H1(str) that hashes a string to a point where it is not feasible to figure out the number that inverts it.

e(a G1, b G2)
=
e(G1, G2)^{a*b}
=
e(b G1, a G2)
# we can easily combine attributes from users,
# because signatures are not watermarked.
# This is a bad privilege escalation.

# alice is an under-aged US citizen.
# bob is an adult that is not a US citizen.
# they can collude to unlock (and citizen:us age:adult),
# even though neither one of them is authorized to do so.
# They are willing to leak their certificate secrets
# to each other to perform this privilege escalation.

aliceIsCitizenUS = s * H1("citizen:us")
bobIsAdult = s * H1("age:adult")
filePub = f * G2

e( aliceIsCitizenUS + bobIsAdult, filePub)
=
e( s * H1("citizen:us") + s * H1("age:adult"), f * G2)
=
e( s * (H1("citizen:us") + H1("age:adult")), f * G2)
=
e( H1("citizen:us")+H1("age:adult"), G2)^{s * f}

The scheme above doesn't resist collusion, because same facts produce the same values. But to resist collusion, use different user values

aliceIsCitizenUS = (s - uA) * H1("citizen:us")
bobIsAdult = (s - uB) * H1("age:adult")
alicePub = (s/(s-uA)) * G2
bobPub = (s/(s-uB)) * G2

e( aliceIsCitizenUS + bobIsAdult, alicePub)
=
e( (s - uA) * H1("citizen:us") + (s - uB) * H1("age:adult"), f * (s/(s-uA))*G2)
=
e( s * H1("citizen:us") + s*(s - uB)/(s - uA) * H1("age:adult"), f * G2)

There is a problem here in that (s/(s-uA)) only cancels the first attribute, but not the other. That will cause this to not produce the unlock key. This resists collusion. s-uA is a secret, as is (s/(s-uA)), uA, and s. We multiply by G2 before we give it to a user. s is the master secret for the whole CA. We must be very careful to not use an expression that can be solved for one of these values.

The bad thing that this allows is to make f public. It doesn't seem to be a disaster, but it does allow for this:

choose f=1

e( f * H1("a") + f * H1("b"), s * G2)
=
e( s * H1("a") + s * H1("b"), f * G2)
=
e( s * H1("a") + s * H1("b"), G2)
=
ab1

ab1^n lets us unlock once, and easily exponentiate the value for any nonce. this is because they are all public.

padlock's People

Contributors

rfielding avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar

Forkers

123linchenjin

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.