- Welcome to the presentation,
Central Bank Digital Currency,
Threats and Vulnerabilities.
We will start with a background.
Then we'll move over a detailed
system description of the prototype, system components,
and to point out functions that need protection,
information to secure, and secure communication.
Then we will deep dive into vulnerabilities
that we have found in the retail central bank
digital currency prototype for phase one,
and then I have to talk about everything else
that needs to be handled
before going to production.
And we'll end with a short description of the solutions
and an even shorter summary.
There will be lots of slides
and lots of information,
and I have to talk really, really fast.
So don't forget to note down interesting slides
if you want to go back
and actually understand what I have said.
So where to start?
My name is Ian Vitek.
I'm born in Stockholm, Sweden,
and starting doing penetration tests, 1996.
I moved from being consultant
to work at a big retail banking company in Sweden
and started with the Computer Emergency Response Team.
And after a couple of years,
I moved into information security.
I've been working at the Riksbank for eight years,
but the last two with the central bank digital currency.
My interests are web application security
and network layer too.
I'm a writer of MACOF.
I love party tricks like DMA attacks,
and local ping bypass attacks,
and other attacks you can do at the bar counter.
I also have attended DEFCON many years.
My first DEFCON was DEFCON 6,
and I'd been speaker and helped out
at DEFCON with other things during the years.
The Central Bank of Sweden, Sveriges Riksbank
is a very small central bank.
It's just maybe 350 employees.
I have also a small disclaimer,
the views and opinions expressed
in the presentation are mine,
and do not necessarily represent the views
and opinions of the Riksbank.
So the e-krona project.
Why central bank digital currency?
I will not talk about the politics here,
but I have to mention that the physical cash
in Sweden has been declining.
Commercial banks has a very good
and widespread digital ID,
and also a payment service called Swish.
And Swish is connected to one of your bank accounts,
so it's very easy to do person-to-person transfers,
and now, we also can do payments in stores.
So in Sweden, most of the transfers
are bank transfers, card payments,
or Swish transfers.
The Central Bank of Sweden
thinks there should be a digital
alternative to physical cash.
The e-krona project started at the Riksbank,
and we want to run a pilot to see
how a central bank digital currency should work.
So we first started with a procurement.
We had some requirements, of course.
We didn't have requirements
of the technology that should be used,
but we have security requirements,
functional requirements, like user stories,
and non-functional requirements.
And there were a winning company.
And the winning company has to develop the prototype.
And I will talk about that prototype
of phase one at year one.
We are still working on the prototype,
but we call it now, phase two.
But this presentation is only
focused on the phase one prototype.
The goal of this presentation is to share insights
of the security challenges of building a prototype
of a two-tier retail central bank digital currency
based on blockchain and value-based tokens.
So we'll now go into the detailed
system description of the prototype.
So the prototype in phase one is a blockchain
with value-based tokens in a two-tier model.
So we have user, we have an app.
In the app, there are logic, of course,
and we have stored information
in the payment instrument.
The payment instrument could be a mobile phone,
but we have also developed a payment card,
a smart card that can handle these payments.
So the logic in the app is that
it needs to be secured is for example, a PIN,
PIN handling, signing the transaction encryption,
and on the disk of the application,
we store, of course, the PIN verification.
The PIN, we can call it as PIN.
And private keys for the tokens,
authentication keys, and message keys.
So we have lots of things in the app
and on the mobile or payment device,
payment instrument that needs protection,
but we're not talking about anything
about the tax against the user
or user interface.
And the app will connect
to the payment service provider's business logic.
So the arrows here in the presentation
is that the arrows also
has a need some type of protection, of course.
So in the payment service provider's business logic,
we have authentication, push messages like Firebase.
If you want any limits,
like how big transfers could be
or how many transfers you can do,
this is limits in the business logic.
And we have lots of back office functions here.
And this is stored in the database
and also on the disk, of course,
but I just write database here.
So we need the storage of authentication keys,
payment history, customer data,
message keys, and so on somewhere.
And all these functions
and information need protection.
But the business logic communicates
with a payment service provider's Corda node.
And the Corda node handles the security
logic of token transactions, token verification,
and wallet management.
And the database of the Corda node,
the payment service provider's Corda node,
they, of course, need have the user public keys,
wallets, tokens, backchain,
I will come back to what backchain is,
and lots of certificates.
But you can't do anything
if you don't have any money or tokens in this case.
So at the Riksbank, also has a Corda node,
and the security logic here is issue
and redeem token verification,
and Corda network management.
The information is handled on that Corda node.
We will have to secure the public keys
and backchain certificates.
But when dealing a transaction, the consensus
mechanism of the Corda is a Corda notary.
So this is a consensus mechanism
in the prototype of phase one.
And then if you are doing a transaction,
you have to check for double-spends.
So the Corda notary prevents double-spends,
and it also signs that transaction.
Through that Corda node, we have a database
where we have keys and certificate,
but we have also the hashes of tokens.
These are the hashes,
these are the tokens that are spent,
information about spent tokens.
So if you can remove tokens from this database,
you can spend tokens once more and do double-spends.
So it's very important that it's secure.
But the Riksbank also has business logic.
We have lots of back office functions,
and we want to be able to pay interests
for the money circulation.
And we also have information about
the outstanding central bank digital currency
that's in circulation.
We also have connections to the Riksbank
real-time gross settlement system.
This is a RTGS system that most central banks have,
but we'll not talk about that
because that has nothing to do about the threat
and vulnerabilities of this prototype.
But to be able to pay, yeah,
I know that technicians love public keys
but normal users, we want some similar way
to do payments like a phone number,
or email address, or an alias, or something.
So we have an alias database,
and that handles add and remove aliases
and mapper alias to a payment
service provider and wallet.
And these aliases and wallets are stored in a database.
And this is also a critical part of the solution
because you can imagine what happens
if you change that all aliases pointed to your wallet.
So of course, it has to be secured also.
But we also have lots of other payment service providers
in our prototype so we can do payment service provider
to payment service provider payments, so yeah.
So what is backchain?
And how to exploit bad implementation?
To explain this in a very simple way,
the Riksbank Corda node,
so we have a Corda node at Riksbank,
and if payment service provider needs tokens, money,
it offer an issue and Riksbank make that issue.
So this is how it looks in a very simple way.
So from nothing, the Corda starts with a transaction,
the actual transaction number here
is a very long binary number,
but I just write one here
as an example to simplify it.
And in that transaction, we have one token,
and the name of the token is the transaction ID
and the index of the token.
So this is the first token in transaction number one.
Amount is 1,000, owner is Payment Service Provider One,
and the Riksbank has signed this transaction
as there are no references from in there.
And then maybe UserA wants to withdraw 200.
Then there's something called token selection.
So Corda will look for a token
or tokens that has 200.
It finds this token and locks it.
That means that no one else can touch
this token during this process.
Then Corda creates another transaction,
this is example number two,
and creates one output token here.
So this token is called two,
and this is the first,
the first token of transaction two.
It's 200, it's owned by UserA.
So this is the public key for UserA here,
and it's signed by Payment Service Provider One,
and it has a reference.
So this token derives from this token.
The first token of transaction one.
But we have change here also.
So UserA gets 200, but the payment service provider
actually creates its own change of 800.
And then that token is called
it's the second token in transaction two,
and its owner is PSP1,
and it's signed by PSP1,
and it also has the same reference points
to that transaction.
Then everything is done.
The first token here is more than spent.
You can't use that anymore.
And then maybe one day,
UserA want to make a transfer to UserB at PSP2 .
So UserA wanted to send UserB 50.
So the token selection here
looks for a token owned by UserA
that could handle this transaction.
So it finds it, locks it,
creates another transaction,
makes one output token,
and also the change to UserA.
This is sent to Payment Service Provider Two.
So Payment Service Provider Two also get these tokens.
So Payment Service Provider Two,
to verify the authenticity,
to see that this is valid,
the Corda node of Payment Service Provider Two
must verify it by looking at all earlier transactions,
but it has only that transaction three,
so Payment Service Provider Two Corda node
will ask for all earlier transaction
leading up to this transaction.
It will ask Payment Service Provider One
for the backchain here.
So Payment Service Provider Two
can verify the references here.
So it points back to this transaction,
to point out to this transaction,
and this is an issue.
So then we have the whole backchain
from the issuing to the transaction number three.
And now, the Corda node of Payment Service Provider Two
can verify all the signatures
and see that the chain is not broken.
This is very important,
and I will come back to this later.
The prototype for phase one
was built on Corda 4.5
and its corresponding token SDK.
And most of the information
is very simplified, of course,
and the bugs in the prototype in the design,
I have verified them in Corda 4.7 also.
So how to exploit token selection on backchains?
So what can a user do
if they want to mess things up?
They can withdraw three and deposit two.
So this is a start.
So when a user wants to withdraw three,
it will take 200 from the PSP1,
and PSP1 will create the change of...
So it receives a token of 197,
but UserA will receive a token of three.
Then it wants to deposit two.
It will looks for a token that can be deposited
or used for depositing,
and will find this token three
and create a new token of one,
that the change back to UserA,
and a token of two.
So the money goes back to PSP1,
two go back to PSP1.
To make it a little bit clearer how this work,
I will clean this up a little bit.
So I move all historic transactions to the left
and adjust all the tokens.
And so what A does is he does
this over and over again.
So he withdraw three, so now, it's 194,
and then he deposit two,
and he gets that, and it's moved over.
And you can do this many, many, many times.
You'll get many tokens, many historic tokens,
and in the end, the UserA deposit all his tokens.
So all the tokens that is I have,
all hundreds of them are deposit to PSP1.
So now, PSP1 have all the historic tokens
and all the tokens that are still valid, not spent.
But sometime in the future,
maybe the administrator of Payment Service Provider One
want to redeem, and move it back to the Riksbank.
Sell the tokens back to Riksbank
to get some Swedish krona instead.
And what happens is that all the tokens
is moved over to the Riksbank Corda node,
and the Riksbank Corda node wants to validate the tokens.
And to do so, it needs the history
and the backchain actually,
of all the tokens that are redeemed.
And this will draw lots of resources
on the Riksbank Corda node.
And the Riksbank Corda node
can actually crash if it's not setup
or the system is not designed correctly.
I will go into the consequences a bit later,
but I want to describe there are other setups
that gives better effect.
So if you're using several issued tokens,
you also can start with several,
you will get several merkle trees.
It's a simple way of describing it.
It's like Payment Service Provider One
has three issued tokens.
Payment Service Provider One
has done one issue of 1,500,
one issue of 2,000, and one issue of 3,000.
And UserA doesn't withdraw 100,
waits for sometime,
and make another withdrawal.
Maybe it will get a token from another issued token.
And then the user splits up his tokens to several.
Maybe it has a friend, UserB.
So UserA sends lots of small transaction to UserB,
and that will split up his tokens
to very many small parts.
And then UserB sends them back to UserA
but in one big chunk.
So now, we are getting a very complex backchain,
and if UserA does this over and over again,
so it also, we have lots of tokens
and very long backchain,
we'll get the TransactionOfDeath,
and what can we do with a TransactionOfDeath?
It can permanently lock tokens
because when you're selling TransactionOfDeath
to someone, it may crash Corda nodes,
and sometimes crashes gave inconsistencies.
So if UserA want to transfer five,
this TransactionOfDeath, this is the five here
is a transaction with many tokens,
and a very long backchain,
and it sends it to UserC
to a payment service provider
that has not seen any of these tokens
or backchains before.
So somewhere in this process,
someone will have to send to the Riksbank Corda node,
please mark this transaction or token as used.
But given this TransactionOfDeath,
I am able to crash both Payment Service Provider One,
the one payment service provider I'm sending from,
but also the receiving party.
So both nodes can crash.
One can crash, two can crash.
It depends on the setup, resources,
and so on, the design.
But when this crash happens,
it also can happen before the Corda nodes
actually mark them as spent.
So in the test that I have done, the Corda,
the Riksbank Corda notary marked
this transaction as used.
This token is used but this token is available
for the payment service provider.
So UserA can't use his wallet anymore
because you look at your wallet,
you see the amount, the balance is just 100.
But when you try to transfer them,
the Corda node will say,
some of them, the tokens in this
transaction are marked as used,
and therefore you can't make any transfer from this wallet.
The wallet is permanently disabled
or it's not functioning anymore as you wanted.
And you have to solve
the inconsistencies of this somehow.
There are other design weaknesses
that you have to look out for.
It's network problems,
timeouts, in-memory token selection,
and locked tokens until restarted.
So card payments in the prototype phase one
is a signed transaction on the smart card
traveling through the payment service provider
of the merchant to the card holder
payment service provider.
And then the transaction is going back
from Payment Service Provider Three
to Payment Service Provider One in this example.
So what I did in the prototype for phase one,
that I did card payments every second or so,
as fast as I could, I did small card payments.
And when I'm doing these card payments,
Payment Service Provider One will communicate
with the Riksbank Corda node to mark,
to say that this token used
in the new card payments is stamped.
But when doing these card payments,
I asked kill the Corda notary process.
But still, in another process,
the Corda notary is down,
I still continuing to doing card payments.
So now, errors start to happen.
So Payment Service Provider One will get timeouts
and will send errors back
to Payment Service Provider Three,
and they will say, "Some of the tokens
"you are using has not been spent
"because you have timeouts."
But when I start up the Riksbank Corda notary,
I still continuing to do card payments,
and somewhere here, it get mixed up.
So some of the transactions will be marked as used
but in memory of the Payment Service Provider Three,
the tokens are still marked as available.
This is very easy to fix.
You have to restart the PSP3 Corda node,
and the tokens will get updated in the correct way,
and marked as spent here also
at the Payment Service Provider Three.
But these are very complex processes,
and you have to be sure that the right tokens
are marked spent where,
and when you should do that
to be able to not have this
type of weaknesses in the system.
And there were some people out there, ING,
that found out that also an evil
payment service provider can lock tokens
that other payment service providers has.
So in this example, UserA want to send five
to UserC at Payment Service Provider Three.
Normal transaction, so A sends tokens
to UserC at Payment Service Provider Three,
and that Payment Service Provider Three
will mark that token as spent.
So the token in this transaction is spent,
and it's marked in the Riksbank Corda notary.
Everything is fine but...
So the Token#2 is available, of course,
and Token#1 was spent in the transaction.
Everything is fine.
But as you are the evil payment service provider here,
you actually have information about token number two,
so you can send a transaction to the Riksbank
Corda notary node to say,
"Well, Token#2 is also spent."
So now, Token#2 is spent,
and the UserC can't use his token anymore
because Riksbank notary node
has marked it as spent.
This is how the setup for the prototype
for the Riksbank phase one is done.
It's a non-validating notary node.
It can be solved by changing
to a validating notary node,
and then you can't do this attack anymore.
And we have to take this into account too.
And also have an ending note on token selection.
So if a payment service provider
want to make a redeem,
in this example, the Payment Service Provider One
has three tokens in its wallet,
owned by Payment Service Provider One.
So there's one token of two million,
one token of one million,
another token of one million.
So the administrator want to make
a redeem of 3 1/2 million.
But if the user, just milliseconds before,
want to make a withdrawal of 50,
the Corda will start doing that
by locking one the tokens.
And Corda will tell the administrator,
"Well, you don't have 3.5 million right now
"because you only have three million available.
"You have to try again later."
But the UserA will get his 50.
This is not a big problem
if you just has one user,
but if you have millions of user
that always is trying to withdraw,
it will be very hard to do some
of the back office functions.
You have to have a design that handle this.
It just was the parent thesis
of the challenges that we have had.
Then now, we'll move over to privacy problems.
So as we talked about before about the backchain,
to be able to verify the authenticity
of the tokens, all historic transactions
for that token is needed.
For PSP to verify this token,
it need all the historic transactions.
So PSP2 can see how PSP1 have done one issue
and how UserA withdrawn 200 for paying
this token to a user on PSP2.
But older and longer backchains reveals more.
So what can an administrator of PSP2 see?
Well, if that PSP2 get one transaction of 60,
to verify the authenticity of this token,
it needs all the historic transactions
deriving up to this token.
So where does this 60 comes from?
So the payment, the Corda node
of Payment Service Provider Two
will get the backchain to see that
UserG from PSP1 has sent 50,
a token of 50 to UserA,
and UserC from PSP3 has sent a token of 20 too.
So this 60 contains, doesn't matter,
20 from this token, and 40 from this token,
or maybe 50 from this token,
and 10 from this token.
Doesn't matter, but both of these tokens
were used in this transaction here.
But where does these tokens comes from?
So the Corda node will go out on the Corda network
and get all the backchain.
And the backchain is all the transaction
arriving to this token,
and it needs to go back to the issuing.
So it will continue going back and back.
So we will see that the token of 50
comes from another token of 50,
that comes from a transaction of 325.
And that user, 325,
comes from three other tokens.
And then we coming back to the issuing here.
So when we have the whole chain back to the issuing,
we have all the transaction leading up to this token.
Now, that Corda network can verify
the authenticity of the token
by verifying all the signatures,
see that the chain is not broken.
But the administrator of Payment Service Provider Two
can also go into the Corda node
to see all these historic transactions
to deal with this more practically.
So I have written some small scripts
to be able to visualize the backchain
and the historic transactions.
So in this practical example,
I've been running the Corda node of PSP2,
and I'm an administrator of Payment Service Provider Two,
and when all the information
from the Payment Service Provider Two Corda node,
and the business layer of Payment Service Provider Two,
I've been able to get the backchain and visualize it,
and I will practically show you how this is done.
So what we have to do is extract the backchain,
get that all the transactions in the backchain,
maybe do some data mining,
and then visualize it.
So this is how you should do that.
So to get the backchain,
you log into the PSP2 Corda node,
and in the Corda process also has a Corda shell.
So you log into the PSP2 Corda node
and start the Corda node shell,
and then you run the command,
internalVerifiedTransactionsSnapshot.
When running this command,
Corda will output all verified transactions.
All transaction it knows about.
And also, about all the backchain transactions.
It's just a mess but I just, for example,
points out one transaction here.
This is how one transaction
from that command looks like.
So we have the transaction ID first, transaction ID.
We have also the signer of this transaction.
So we know who is a payer here.
So this is the from address,
and here are the inputs of this transaction.
And this shows the tokens from the input,
it can be one or more tokens.
And the naming here is this is an early transaction,
and this is a token.
The first token in this old transaction
was the input token for this transaction.
And then we have output tokens.
The first output tokens is always the payee
who gets the transaction.
So this is the public key of the payee,
and here, we can see here, the amount.
If there are a second output,
it is a change that the owner
of the token gets back.
You can see the public key of the change
is the same of the signer of that transaction.
So what we do now is just write a small script
that takes out the payer, the from,
and who is the payee receiving the amount.
That's all what we need,
But I also take out if there are any inputs,
if there are no inputs in this transaction,
I know it's a transaction from the Riksbank
because the Riksbank is the only one
that can issue transactions.
When you're issuing transactions,
there are no input tokens.
I can also look at the public key.
If the public key is a couple of bytes shorter,
it's in our prototype for phase one,
it's a payment service provider.
So users has a little bit longer keys.
So now, we can see who's the Riksbank,
who's the payment service providers,
and what users.
So I just extract all those transaction
and creates a JSON file.
So yes, that's from this public key
to this public key and the amount.
So it's just thousands of transactions in the JSON file,
and that's all we need right now.
But you maybe want to have wallet IDs.
And wallet IDs can be found in the business layer
of the Payment Service Provider Two.
So if you have an app,
you want to go see the transaction history,
your historic transactions,
and that's stored in the business layer.
So the administrator of the Payment Service Provider Two
can go in and look there.
So this is the user historic record extract
from the payment to business layer.
So here, we have the transaction ID,
and actually, that's the same transaction ID
that was in the backchain.
So here, we can map the payer and payee wallet ID,
but there are other ways to do it also,
because if one of my user has received a payment
or sent a payment to someone,
I have their wallet ID in my
transaction history, of course.
And then in Corda, you always can look up
the public key for a wallet.
So if I just look at my transaction history,
there are lots of wallet IDs,
and I can just look up all
the public addresses I know of,
and then map them, therefore enrich the JSON file.
So now, I have enriched JSON file
and just pulled that into a HTML file.
And with a D3.js, Data-Driven Documents
library called D3.js, here's a function
called D3.layout.force,
and they just connect all those arrows,
all those transactions from A to B,
B to C automatically.
So in my HTML file, I just say,
I just points at the JSON file
where I have the enriched transactions,
I set up the graph with the arrows,
and just print it,
and it would look like this.
And so, this is how the webpage look like.
So I have here, I can detect the Riksbank.
I can see one of the historic transaction
to a payment service provider.
So this is an issue.
So this payment service provider
has done an issue of one million.
And from that one million,
one user called this wallet ID on PSP1
has withdrawn 1,337.10.
So then I know this payment service provider
is Payment Service Provider One.
So this wallet has another transaction,
so let's see if I can get hold of it, no?
To this Payment Service Provider Three,
and there has been two transactions
to this payment service provider,
the wallet on this Payment Service Provider Three,
and it's one transaction of 1,337.40 and 1,337.50.
Then it comes to my Payment Service Provider Two here
to my user here at the Payment Service Provider Two.
But when I received this payment of 1,337.60,
I also get all historic backchains,
so I can now see all the historic
transactions leading up to this.
Yeah, so backchain and privacy.
First, I have to say,
even if you don't have the wallet ID,
you have always the public key.
And public keys are probably
also personal identifiable information.
And whichever technology you choose,
you have to take this into account,
and for some blockchain technologies,
this can be challenged.
So we have to fix this in phase two.
Then we have everything else.
Everything else that I've not talked
about in this presentation,
and everything else that has to be sold
before going into production
with a token-based retail central banking currency.
So we have performance
and the verification of the tokens.
We have to store, if you have lots of lots of tokens
or very long backchains.
It doesn't matter what technology you are using,
you have to verify the authenticity
and have the performance.
And we have also, a challenge for the high availability
and in-memory token selection.
So all the token selection is done
in the memory of the Corda node,
so how should you do 24/7 service
like if you're gonna upgrade Corda or operating system?
You can't just shut down the Corda node
and disable payments during this time.
And how should you do resolve
the recovery after catastrophic failures?
You can't just take a database backup
and restore it because you're missing
all the transactions leading up
to that catastrophic failure.
So you need some kind of area, storage,
where you store all the transactions
that you can build up or in other ways,
fix the inconsistency when restoring a Corda node.
And then secure offline.
Can that been done?
We have to look at it,
and we will look at it.
And we have problems with non-repudiation,
meaning whether it's authorization.
You have to be sure it is right person
that has done the transaction,
and it has to be proof of that
that this user has done this transaction.
And then we have another thing
regarding information security,
like access control, operational security,
incident management, fraud detection,
AML, new customer.
We have all the IT security to solve
or has to be in place before going in production.
And of course, compliance, laws,
regulation, financial compliance,
and there are lots of solutions.
There are many solutions for the presented challenges.
So I have talked about lots of challenges
but there are lots of solutions.
I will not go into the solutions in this presentation.
There are chain snipping, chipping, key rotation,
zero knowledge proof, and other encryption
to solve some things,
and we have validating notary nodes
to solve other things.
We have hardware wallets.
Maybe could that work with offline?
And restore procedures and functions
for correcting inconsistencies.
Maybe we can fix everything.
It's a technical project
and we probably can solve it technically.
And Riksbank is now experimenting with other designs,
and we'll also look at other technologies.
But I also have to say that every solution
will have their own consequences.
So if we just solve one thing,
maybe we have other problems with it
like performance or other things.
I also want to say that during phase one,
we had a design where the private key
was on the payment instrument
but the tokens were on the Corda node.
So to experiment with the payment service
directly to PSP2, we are trying
to have both the key and the tokens
on the payment service provider,
and we're also looking for offline functionality,
and then we will experiment with having the key
and the tokens on the payment instrument.
And so, we can do the transfer of tokens offline
to another payment instrument device.
So we're looking at lots of other
fun things during phase two,
and we're not ready for production anytime yet.
And we probably have to change
the laws of the Central Bank in Sweden,
the Riksbank law before going into production anyway, so.
But we will still look, still looking
at the central bank digital currency
and other technologies.
So the summary, the goal of this presentation
is to share insights of the security challenge
of building a prototype of a two-tier
retail central bank digital currency
based on a blockchain with value-based tokens.
I have only presented threats, vulnerabilities,
and security fails, and some unknowns.
I have not presented all the good design
and all the positive lessons learned.
That's another presentation.
So we have invented and built so much
good working parts, components, solution design,
and we have learned so much going for,
when we're now going forward,
and we'll look how we should build a system
that could be run in production in Sweden.
But I've not talked about that
in this presentation.
That's the bad stuff, okay?
Thank you for attending.