[cap-talk] Mailkey works!
David Hopwood
david.hopwood at industrial-designers.co.uk
Thu Jun 7 15:52:15 EDT 2007
Mark Miller wrote:
> There was one critical fact about email that I hadn't known, that was
> preventing me from understanding [the mailkey] protocol. You are implicitly
> making use of the ability of Bob to compare
>
> alice+3a66fo at op.nu
>
> to
>
> alice+4d7fb1 at op.nu
>
> and conclude that they both communicate to the "same" entity, for some
> meaning of same. The comparison here needs the security properties of
> an authenticating grant-matching equality primitive (EQ), even though
> these represent two different capabilities which grant different
> authority.
The methods of doing this that we discussed were:
- having the system provide a "weak equality" operation primitively;
- using (Token, reference) pairs in place of plain references.
The code below implements "weak equality" nonprimitively, and without changing
the implementation of references.
pragma.syntax("0.9")
def makeBrand := <elib:sealing.makeBrand>
# Object-capability implementation of addresses similar to mailkey's
# <name+key at domain>. Any mailkey-like protocol can be implemented on top
# of this fairly easily.
#
# (I'm not entirely sure that this is what we need, and I can't test it at the
# moment because my E installation is broken, but I thought I'd post it anyway
# so we can discuss it at the Friday meeting.)
#
# An address object acts as a forwarder to an arbitrary target object. We
# can generate a new address for any target, and compare two addresses to
# see whether they forward to the same target. We can also separately revoke
# any address (for simplicity, this uses the caretaker pattern rather than a
# membrane).
# Start with the non-distributed case, where we can assume that the address
# comparer is universally trusted.
def nonDistributedCase {
# E doesn't have built-in sibling communication; simulate it using a
# sealer/unsealer.
def [siblingSealer, siblingUnsealer] := makeBrand("sibling")
to makeAddressAndRevoker(var target) :[any, Thunk] {
def address {
to getSealedTarget() :any { return siblingSealer.seal(target) }
match [verb, args] { return E.call(target, verb, args) }
}
def revoker() { target := null }
return [address, revoker]
}
to weakEq(a, b) :boolean {
try {
return siblingUnsealer.unseal(getSealedTarget(a)) ==
siblingUnsealer.unseal(getSealedTarget(b))
} catch _ { # FIXME: more specific catch
return false
}
}
}
# To remove the assumption of a universally trusted comparer, we have each domain
# do comparison for its own objects. A domain can be any unit of distribution.
#
# For simplicity, this code is written to use immediate calls, even though it
# really only makes sense in a distributed setting. In practice this functionality
# would probably be built-in to the distribution protocol, making use of vats'
# public/private keys instead of [domainSigner, domainVerifier].
def distributedCase {
to makeDomain(name :String) {
# Sealer/unsealer pairs can be used either with the unsealer private, for
# encryption, or with the sealer private, for authentication. Here we are
# using them for authentication.
def [domainSigner, domainVerifier] := makeBrand(name)
# Same as the non-distributed case.
def [siblingSealer, siblingUnsealer] := makeBrand(`$name sibling`)
def domain {
to getVerifier() :any { return domainVerifier }
to makeAddressAndRevoker(var target) :[any, Thunk] {
def address {
# If we seal [x, y] using domainSigner, we are asserting that x and y
# both belong to this domain and are (forever) weakly equal.
# I'm sure there is probably a simpler way of doing this.
to getProofOfDomain() :any {
return [domainVerifier, domainSigner.seal([address, address])]
}
to getProofOfWeakEqualityTo(other) :any {
def [otherVerifier, otherProof] := other.getProofOfDomain()
try {
if (otherVerifier != domainVerifier ||
otherVerifier.unseal(otherProof) != other ||
siblingUnsealer.unseal(address.getSealedTarget()) !=
siblingUnsealer.unseal(other.getSealedTarget())) {
return null
}
return [domainVerifier, domainSigner.seal([address, other])]
} catch _ { # FIXME: more specific catch
return null
}
}
to getSealedTarget() :any { return siblingSealer.seal(target) }
match [verb, args] { return E.call(target, verb, args) }
}
def revoker() { target := null }
return [address, revoker]
}
}
return domain
}
to weakEq(a, b) :boolean {
try {
def [aVerifier, aProof] := a.getProofOfDomain()
def [bVerifier, bProof] := b.getProofOfWeakEqualityTo(a)
return aVerifier == bVerifier &&
aVerifier.unseal(aProof) == [a, a] &&
bVerifier.unseal(bProof) == [b, a]
} catch _ { # FIXME: more specific catch
return false
}
}
to getDomainVerifier(a) :any {
def [aVerifier, aProof] := a.getProofOfDomain()
if (aVerifier.unseal(aProof) != a) {
throw `$a failed to prove membership of $aVerifier`
}
return aVerifier
}
}
# My current E installation is too old for 'pragma.syntax("0.9")' to work,
# and I can't download a new one because cypherpunks.to is down. Still, here
# are some tests for how it's supposed to work:
def x := "x"
def y := "y"
def domA := distributedCase.makeDomain("A")
def [x1, x1revoke] := domA.makeAddressAndRevoker(x)
def [x2, x2revoke] := domA.makeAddressAndRevoker(x)
def [y1, y1revoke] := domA.makeAddressAndRevoker(y)
def domB := distributedCase.makeDomain("B")
def [x1b, x1brevoke] := domB.makeAddressAndRevoker(x)
distributedCase.weakEq(x1, x2)
# true
distributedCase.weakEq(x1, y1)
# false
distributedCase.weakEq(x1, x1b)
# false
# Try (and fail) to create a fake "x" weakly equal to x1, without using domA.
[verifier, proof] := x1.getProofOfDomain()
def fakex {
# these are the wrong proofs; we can't create the right ones without
# access to domA's domainSigner.
to getProofOfDomain() :any { return [verifier, proof] }
to getProofOfWeakEqualityTo(_) :any { return [verifier, proof] }
match [verb, args] { println "bwahaha!" }
}
distributedCase.weakEq(x1, fakex)
# false
distributedCase.getDomainVerifier(fakex)
# should throw exception
x1revoke()
x1
# should throw exception
--
David Hopwood <david.hopwood at industrial-designers.co.uk>
More information about the cap-talk
mailing list