Cryptographic Algorithms for Proxy Re-Encryption (PRE), Transform Cryptography and Orthogonal Access Control with Implementation Notes Using Scala and Functional Programming.
(Presented at DEF CON 26)
12. @ironcorelabs
Bob Wall
@bithead_bob
Adding a Recipient
Random
AES Key
Carol’s
Public Key
Random
AES Key
Alice’s
Public Key
Bob’s
Public Key
Random
AES Key
Random
AES Key
Document
13. @ironcorelabs
Bob Wall
@bithead_bob
Orthogonal Access Control
Build a system that:
Allows users to decide which groups are allowed to access data
Independently allows group administrators to control who belongs
to those groups
Relies on cryptographically backed access control, rather than
policy-based controls
Makes each change to group membership, access grant, or access
revocation a constant-time operation independent of number of
users, groups, documents
14. @ironcorelabs
Bob Wall
@bithead_bob
Proxy Re-Encryption (PRE)
Set of cryptographic algorithms based on public key
encryption - often pairing-based cryptography
Originally designed to allow the recipient of an
encrypted message to delegate access to another
party without sharing her private key
15. @ironcorelabs
Bob Wall
@bithead_bob
PRE Primitives
PRE algorithms typically include five cryptographic
primitives:
1.Key Generation
2.Transform Key Generation
3.Encryption
4.Transformation (ReEncryption)
5.Decryption
18. @ironcorelabs
Bob Wall
@bithead_bob
PRE for Orthogonal Access Control
Introduce the concept of a group
Create a group
Encrypt document to the group
Add a member to the group
Allows immediate access to document without
requiring any modification
Remove a member from the group
Immediate revokes access, also without requiring
modification
24. @ironcorelabs
Bob Wall
@bithead_bob
Improving Security
User will use one or more devices to generate or
access data
Instead of sharing user’s private key across devices,
add another layer of delegation, from user to device
Device private keys always stay on device
Device access can be revoked if device is lost or
compromised
29. @ironcorelabs
Bob Wall
@bithead_bob
Device Requests Access to Document
Proxy searches for shortest path of transforms from
doc to device
Doc shared with user, user approved device
Doc shared with group, user belongs to group, user
approved device
Proxy applies transforms in succession to generate
doc encrypted to device
Device decrypts using private key
30. @ironcorelabs
Bob Wall
@bithead_bob
Tada! Scalable End-to-End Encryption
Can handle arbitrarily large groups of users
Granting or revoking access is constant-time
operation
No need to copy private keys all over the place
31. @ironcorelabs
Bob Wall
@bithead_bob
PRE to Protect User PII
User’s
Private Data
User
Public Key
Encrypted
Data
User
Private Key
Company
Public Key
Transform
Key
User to
Company
32. @ironcorelabs
Bob Wall
@bithead_bob
PRE Library
We have implemented the PRE primitives in a
Scala library
We use ScalaJS to generate a client-side
Javascript library from the same source
Library is open source, available on GitHub
- IronCoreLabs/recrypt
33. @ironcorelabs
Bob Wall
@bithead_bob
Working System
We built a Javascript SDK around the library
SDK talks to a service that functions as the
public key repository and transformation proxy
Developers are free to try the system -
https://docs.ironcorelabs.com has a Getting
Started example
34. @ironcorelabs
Bob Wall
@bithead_bob
Pairing-Based Cryptography
The PRE algorithms we selected are built using
pairing-based cryptography, which is in turn built
using elliptic curve cryptography.
Elliptic curve crypto is pretty standard today, but
pairing-based crypto is less frequently
encountered.
35. @ironcorelabs
Bob Wall
@bithead_bob
Bilinear Pairings
The basic operation in pairing-based crypto is the
bilinear pairing (also called a bilinear mapping).
This is a function defined as follows:
e : G1 x G2 -> GT
That is, e takes one argument from a group G1 and
one argument from a group G2 and produces a value
that is in another group GT
36. @ironcorelabs
Bob Wall
@bithead_bob
Properties of Bilinear Pairings
For e to be bilinear, it needs to satisfy these
properties:
e(R + S, T) = e(R,T)e(S,T)
e(R, S + T) = e(R,S)e(R,T)
e(aS,bT) = e(S,T)ab = e(S,T)ba = e(bS, aT)
for all positive ints a,b
37. @ironcorelabs
Bob Wall
@bithead_bob
How We Use Pairings
e : G1 x G2 -> GT
G1 is the group of points E[Fp]
G2 is the group of points E’[Fp2]
GT is the group containing the rth roots of unity
in Fp12. Note that these values are not points.
All three groups have order r.
38. @ironcorelabs
Bob Wall
@bithead_bob
Using Pairings for Crypto
The bilinearity properties allow you to swap the
scalar multiples of two points. For instance, an
elliptic curve key pair is
pk = g1 * sk
That is, the public key is just the secret key
times a fixed generator point g1 in G1.
39. @ironcorelabs
Bob Wall
@bithead_bob
Pairing-Based Encryption
Suppose Alice generates a keypair (skA, pkA), Bob
generates (skB, pkB), and both public keys are made
available to anyone.
Alice can encrypt a message m to Bob by computing
em = m * e(pkB, skA * g2)
where g2 is a generator point in G2.
Bob can recover m by computing
m = em * e(pkA, -skB * g2)
40. @ironcorelabs
Bob Wall
@bithead_bob
The Magic of Bilinearity
This works because
m * e(pkB, skA * g2) =
m * e(skB * g1, skA * g2) =
m * e(skA * g1, skB * g2) =
m * e(pkA, skB * g2)
And
m * e(pkA, skB * g2) * e(pkA, -skB * g2) =
m * e(pkA, (skB - skB) * g2) = m
41. @ironcorelabs
Bob Wall
@bithead_bob
A Note on Security
The security of pairing-based crypto relies on two
hard problems: the Elliptic Curve Discrete Log
Problem (protects secret keys when you know public
keys) and the Discrete Log Problem, which protects
the pairing itself.
The latter is because the group GT is not a set of
elliptic curve points, but a group over a finite
field.
42. @ironcorelabs
Bob Wall
@bithead_bob
Security (cont.)
Recent advances in the use of Number Field Sieves
to attack the Discrete Log Problem, and some
improved analysis of those attacks, have led
researchers to conclude that the equivalent
security of the degree 12 extension of a 256 bit
prime field is under 100 bits.
Estimated that getting back to 128 bit equivalent
security takes a prime of at least 461 bits.
57. @ironcorelabs
Colt Frederickson
@coltfred
Api.scala Example.scala
for {
plaintext <- api.generatePlainText
encrypted <- api.encrypt(plaintext,
itGroupPub,
bobPublicSigningKey,
bobPrivateSigningKey)
} yield encrypted
def generatePlaintext: IO[Plaintext]
. . .
def encrypt(
plaintext: Plaintext,
toPublicKey: PublicKey,
publicSigningKey: PublicSigningKey,
privateSigningKey: PrivateSigningKey
): IO[EncryptedValue]
def decrypt(
encryptedValue: EncryptedValue,
Encrypt value to IT group
58. @ironcorelabs
Colt Frederickson
@coltfred
Api.scala Example.scala
IT hires Charlie!
for {
//Hire Charlie, so she generates her keys.
//This happens on her own device, but for demo
//we’ll generate them here.
(charliePrivateKey,
charliePubKey) <- api.generateKeyPair
//"add" her to the group
itGroupToCharlieTransform <- api.generateTransformKey(
itGroupPriv,
charliePubKey,
bobPublicSigningKey,
} yield itGroupToCharlieTransform
def generateTransformKey(
fromPrivateKey: PrivateKey,
toPublicKey: PublicKey,
fromPublicSigningKey: PublicSigningKey,
fromPrivateSigningKey: PrivateSigningKey
): IO[TransformKey]
def transform(
encryptedValue: EncryptedValue,
transformKey: TransformKey,
publicSigningKey: PublicSigningKey,
privateSigningKey: PrivateSigningKey
): IO[EncryptedValue]
59. @ironcorelabs
Colt Frederickson
@coltfred
Api.scala Example.scala
Hiring again, rinse and repeat.
for {
//Hire Alice, so she generates her keys.
(alicePriv, alicePub) <- api.generateKeyPair
//"add" her to the group
itGroupToAliceTransform <- api.generateTransformKey(
itGroupPriv,
alicePub,
bobPublicSigningKey,
bobPrivateSigningKey)
} yield itGroupToAliceTransform
def generateTransformKey(
fromPrivateKey: PrivateKey,
toPublicKey: PublicKey,
fromPublicSigningKey: PublicSigningKey,
fromPrivateSigningKey: PrivateSigningKey
): IO[TransformKey]
def transform(
encryptedValue: EncryptedValue,
transformKey: TransformKey,
publicSigningKey: PublicSigningKey,
privateSigningKey: PrivateSigningKey
): IO[EncryptedValue]
60. @ironcorelabs
Colt Frederickson
@coltfred
Api.scala Example.scala
Alice decrypts
for {
transformedData <- api.transform(
encryptedValue,
itGroupToAliceTransform,
transformingServicePubSigning,
transformingServicePrivSigning)
//For this demo, convert the error into an
//exception if it fails
decryptedData <- api.decrypt(
transformedData,
alicePriv).toIO
} yield decryptedData
def transform(
encryptedValue: EncryptedValue,
transformKey: TransformKey,
publicSigningKey: PublicSigningKey,
privateSigningKey: PrivateSigningKey
): IO[EncryptedValue]
. . .
def decrypt(
encryptedValue: EncryptedValue,
privateKey: PrivateKey
): Either[String, Plaintext]
63. @ironcorelabs
Colt Frederickson
@coltfred
What Else?
- Multi-hop means you can do really neat things!
- Each user can have a master key and many devices
which makes the loss of a "device key" a non issue.
You just forget the device transform key to revoke
access.
- Each group can have sub groups, which allows for
solving problems with GDPR, such as right to forget.
- Key rotation could be encoded by generating
transform keys between your old key and the new one.