<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="http://flow.gi/feed.xml" rel="self" type="application/atom+xml" /><link href="http://flow.gi/" rel="alternate" type="text/html" /><updated>2023-09-25T07:40:57+00:00</updated><id>http://flow.gi/feed.xml</id><title type="html">Make or break</title><subtitle>Security research, software development and thing making</subtitle><entry><title type="html">Single use vaping seriously harms everyone on the planet</title><link href="http://flow.gi/SingleUseVapLandfill/" rel="alternate" type="text/html" title="Single use vaping seriously harms everyone on the planet" /><published>2023-09-24T00:00:00+00:00</published><updated>2023-09-24T00:00:00+00:00</updated><id>http://flow.gi/SingleUseVapLandfill</id><content type="html" xml:base="http://flow.gi/SingleUseVapLandfill/">&lt;p&gt;One of my friends is into vaping and has recently switched to single-use e-cigarettes, which resemble the normal smoking experience: you can buy them over the counter or from a vending machine, it comes packaged with all the warning signs, it is self-contained (no need to charge, refill, etc.), just unwrap the package, vap and when you are done you just throw it away. In summary: convenience. I am not going to go into the health aspects of vaping, anyone is free to do what they want with their body - but instead on the impact it has on the environment and, by extension, everyone else. I guess you could summarise as “Single use vaping seriously harms everyone on the planet”.&lt;/p&gt;

&lt;h1 id=&quot;getting-into-the-e-cigarette&quot;&gt;Getting into the e-cigarette&lt;/h1&gt;

&lt;p&gt;For our analysis we have chosen one of the brands that can be easily found available on the street in Gibraltar, “Lost Mary”. The device comes packaged in a cardboard box roughly the size of a cigarette pack.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-pack.png&quot;&gt; &lt;img src=&quot;/images/vap-pack.png&quot; alt=&quot;&quot; title=&quot;Single use e-cigarette pack&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the actual e-cigarette measuring 66mm tall x 35mm wide x 16mm thick. The box claims that the e-cigarette lasts for up to 600 puffs.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-pods.png&quot;&gt; &lt;img src=&quot;/images/vap-pods.png&quot; alt=&quot;&quot; title=&quot;Single use e-cigarette&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The exterior body of the e-cigarette unit (marketed as a “pod”) is made up of a metallic main body and hard thermoplastic puffing at the top plus a cover at the bottom. It should be easy to disassemble by removing the plastic cover, however for our analysis we decided to open it with a grinder so that we can see the interior:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-internals.png&quot;&gt; &lt;img src=&quot;/images/vap-internals.png&quot; alt=&quot;&quot; title=&quot;Internals of the single use e-cigarette&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The e-cigarette is broken down into clearly separated areas which we discuss in the following sections.&lt;/p&gt;

&lt;h2 id=&quot;liquid-container&quot;&gt;Liquid container&lt;/h2&gt;

&lt;p&gt;The units I used for analysis had been already “smoked” so most of the liquid was gone, although when new it comes with 2ml of a chemical compound that, according to the package, is FATAL when in contact with skin, so if you are going to open one of these units please take appropriate protections.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-danger.png&quot;&gt; &lt;img src=&quot;/images/vap-danger.png&quot; alt=&quot;&quot; title=&quot;Danger notice in single use e-cigarette&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The liquid is kept in the soaked sponge, which is protected by a transparent hard plastic case. This, heated up, is what goes into the lungs.&lt;/p&gt;

&lt;h2 id=&quot;heating-element&quot;&gt;Heating element&lt;/h2&gt;

&lt;p&gt;I was expecting a more complex mechanism, but the simplicity surprised me. The heating element is a peeled-off cable, wrapped around a cigarette filter-like material that acts as a wick for the liquid stored in the container. The wick pulls the liquid through capillarity to keep it wet, and when electricity flows through the cable / resistor / coil it heats up and creates the water vapour infused with the liquid:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-wick.png&quot;&gt; &lt;img src=&quot;/images/vap-wick.png&quot; alt=&quot;&quot; title=&quot;Wick of the single use e-cigarette&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;battery&quot;&gt;Battery&lt;/h2&gt;

&lt;p&gt;The largest component in the e-cigarette is the battery, a 3.7v 360mAh rechargeable Lithium Ion battery.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-battery.png&quot;&gt; &lt;img src=&quot;/images/vap-battery.png&quot; alt=&quot;&quot; title=&quot;Battery of the single use e-cigarette&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the battery is rechargeable the e-cigarette does not offer any functionality to recharge it, once the battery falls below 3.3v the chip flashes the LED 10 times, then shuts down. This is the signal to the owner to throw this pod away, and go get a new one.&lt;/p&gt;

&lt;p&gt;The difficulty to access the battery makes it nearly-impossible to recycle, sending all its components into &lt;a href=&quot;https://en.wikipedia.org/wiki/Environmental_impacts_of_lithium-ion_batteries&quot;&gt;landfill&lt;/a&gt; (toxic metals such as cobalt, nickel and manganese) and increasing the change of a landfill fire due to short-circuits.&lt;/p&gt;

&lt;h2 id=&quot;electronics&quot;&gt;Electronics&lt;/h2&gt;

&lt;p&gt;To control the heating mechanism the e-cigarette incorporates a small number of electronic components, all of them integrated in an &lt;a href=&quot;https://cmcrisensors.wordpress.com/2019/06/19/characteristics-of-electronic-smoke-pressure-sensor-controller-s085/&quot;&gt;S085 ASIC&lt;/a&gt; or similar:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Electret microphone&lt;/strong&gt;, which works as a pressure sensor to determine when the user puffs on the cigarette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SMD LED&lt;/strong&gt;, to let the user know whether it is operational or has ran out of puffs&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SMD driver chip&lt;/strong&gt;, to coordinate the microphone, LED and heater&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SMD capacitor&lt;/strong&gt;. to filter noise from the battery out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever the user puffs on the cigarette, the air starts flowing and activates the microphone membrane at the bottom of the e-cigarette (which works as a pressure sensor). The driver picks up on this and switches on the LED and sends current to the coil, which heats up the liquid and creates the vapour. When the user stops sucking on the e-cigarette, the microphone membrane stops making contact and the driver goes into low-power consumption mode waiting until the next puf.&lt;/p&gt;

&lt;p&gt;Whenever the battery is low, the LED blinks to let the user know it is time to get a new pod.&lt;/p&gt;

&lt;h1 id=&quot;recycling&quot;&gt;Recycling&lt;/h1&gt;

&lt;p&gt;It is clear that the number and type of components make this a hard to recycle device:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-blowup.png&quot;&gt; &lt;img src=&quot;/images/vap-blowup.png&quot; alt=&quot;&quot; title=&quot;Components of the single use e-cigarette&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pod itself contains plastic, metal, battery, electronics so it will go straight into landfill.&lt;/p&gt;

&lt;p&gt;Each pod weights approximately 25 grams, broken down by the components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Battery: 8g&lt;/li&gt;
  &lt;li&gt;Metal: 7g&lt;/li&gt;
  &lt;li&gt;Plastic: 10g&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looks like the only bit that can be recycled is the cardboard package that the pod comes in.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Clearly disposable e-cigarettes are designed for convenience and not for ease of recycling.&lt;/p&gt;

&lt;p&gt;If we consider that a smoker &lt;a href=&quot;https://cancercontrol.cancer.gov/sites/default/files/2020-06/m7_11.pdf&quot;&gt;takes 15 puffs out of a traditional cigarette on average&lt;/a&gt; and that e-cigarette lasts for 600 puffs, it means that a smoker would smoke 2 packs of 20 cigarettes for each e-cigarette pod. The waste generated by two packs of cigarettes are two cardboard packets, the plastic wrappers and 40 filters.&lt;/p&gt;

&lt;p&gt;Leaving aside the health implications, if you compare the traditional cigarette waste against e-cigarettes, the amount of waste that is produced after you finish smoking it is much worse in the electronic version. Assuming that a smoker of single use e-cigarettes goes through a pod every couple of days this means that over 180 of each of these will end up in landfill - 180 batteries, 180 electronics, 180 pieces of non-biodegradable plastic and metal casing.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/vap-scale.png&quot;&gt; &lt;img src=&quot;/images/vap-scale.png&quot; alt=&quot;&quot; title=&quot;Waste weighing&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is 4.5kg of highly contaminant landfill waste each year produced by each single use e-cigarette smoker person. Please consider the implications for the environment before switching to single-use products.&lt;/p&gt;</content><author><name></name></author><category term="gibraltar" /><category term="making" /><summary type="html">One of my friends is into vaping and has recently switched to single-use e-cigarettes, which resemble the normal smoking experience: you can buy them over the counter or from a vending machine, it comes packaged with all the warning signs, it is self-contained (no need to charge, refill, etc.), just unwrap the package, vap and when you are done you just throw it away. In summary: convenience. I am not going to go into the health aspects of vaping, anyone is free to do what they want with their body - but instead on the impact it has on the environment and, by extension, everyone else. I guess you could summarise as “Single use vaping seriously harms everyone on the planet”.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/norecycle.jpg" /><media:content medium="image" url="http://flow.gi/norecycle.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Making it in cybersecurity</title><link href="http://flow.gi/CareersInCyber/" rel="alternate" type="text/html" title="Making it in cybersecurity" /><published>2022-10-26T00:00:00+00:00</published><updated>2022-10-26T00:00:00+00:00</updated><id>http://flow.gi/CareersInCyber</id><content type="html" xml:base="http://flow.gi/CareersInCyber/">&lt;p&gt;What’s the fastest / funniest / more satisfying / less risky way to make a career in cybersecurity? Every now and then I get asked by colleagues starting in information security what is the best area for them to focus on; this article summarises my views on choosing a successful career in cybersecurity.&lt;/p&gt;

&lt;p&gt;My default answer is to go for the area that you enjoy the most and where you think you can put your skills to work: is it attack or defence skills, compliance work, application security, product visionary… ? This post discusses some of the available paths and roles within those paths to be able to make a living out of cybersecurity.&lt;/p&gt;

&lt;p&gt;My views are based on experience and those of friends and colleagues that have taken the various paths described below, however it is still a subjective view. If you disagree or want to add additional career paths or roles just &lt;a href=&quot;https://github.com/llmora/llmora.github.io/blob/master/_posts/2022-10-26-CareersInCyber.md&quot;&gt;submit a pull-request&lt;/a&gt;, always happy to learn.&lt;/p&gt;

&lt;p&gt;You can sort the following summary matrix based on your goals and skills:&lt;/p&gt;

&lt;div id=&quot;matrix&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;article&quot;&gt;&lt;/div&gt;

&lt;link href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot; integrity=&quot;sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi&quot; crossorigin=&quot;anonymous&quot; /&gt;

&lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;

&lt;script crossorigin=&quot;&quot; src=&quot;https://unpkg.com/react@18/umd/react.production.min.js&quot;&gt;&lt;/script&gt;

&lt;script crossorigin=&quot;&quot; src=&quot;https://unpkg.com/react-dom@18/umd/react-dom.production.min.js&quot;&gt;&lt;/script&gt;

&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css&quot; /&gt;

&lt;script src=&quot;https://unpkg.com/prop-types@15.6.0/prop-types.min.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;https://unpkg.com/ag-grid-community@28.0.0/dist/ag-grid-community.js&quot; crossorigin=&quot;&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;https://unpkg.com/ag-grid-react@28.0.0/bundles/ag-grid-react.min.js&quot; crossorigin=&quot;&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;/assets/js/cyber-careers.js&quot;&gt;&lt;/script&gt;</content><author><name></name></author><category term="security" /><summary type="html">What’s the fastest / funniest / more satisfying / less risky way to make a career in cybersecurity? Every now and then I get asked by colleagues starting in information security what is the best area for them to focus on; this article summarises my views on choosing a successful career in cybersecurity.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/success.png" /><media:content medium="image" url="http://flow.gi/success.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Nikko Hawg 1/10 RC car transmission steering column replacement</title><link href="http://flow.gi/NikkoHawgSteering/" rel="alternate" type="text/html" title="Nikko Hawg 1/10 RC car transmission steering column replacement" /><published>2021-08-23T00:00:00+00:00</published><updated>2021-08-23T00:00:00+00:00</updated><id>http://flow.gi/NikkoHawgSteering</id><content type="html" xml:base="http://flow.gi/NikkoHawgSteering/">&lt;p&gt;The Nikko Hawg RC car piece for the steering-transmission is made of ABS plastic and is held in place through two metal screws which eventually eat into the plastic, loosening the connection and preventing the steering from working. I ran out of replacement screws so decided to 3D print a redesigned piece with nut catchers that would not suffer from the same problem, you can find the STL files on our &lt;a href=&quot;https://github.com/llmora/deprogramming-obsolescence/&quot;&gt;Deprogramming Obsolescence repository&lt;/a&gt; (along with editable OpenSCAD files in case you need to adjust the sizes for other similar Nikko cars). If you modify the designs can you share them back with the community please?&lt;/p&gt;

&lt;p&gt;To be fair with Nikko, this piece has withstood on and off usage over the past 35 years, from well before things had a programmed obsolescence :-)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/nikko_final.png&quot;&gt; &lt;img src=&quot;/images/nikko_final.png&quot; alt=&quot;&quot; title=&quot;Nikko replaced part&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issue is quite easy to diagnose: one of the wheels starts wobbling to the point that the car comes to a stand-still. When you inspect it closely you notice that one of the screws holding the transmission steering column is missing. This happens because the screw is held in place by threads cast into the ABS piece which eventually wear out and are unable to hold the screw in place:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/nikko_fail.png&quot;&gt; &lt;img src=&quot;/images/nikko_fail.png&quot; alt=&quot;&quot; title=&quot;Nikko stripped thread&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new piece has some simplifications over the original design to ease printing, but the key items and strength should be the same. The main change is the addition of an M3 nut catcher which is used to secure the screw, removing the reliance on the plastic:&lt;/p&gt;

&lt;iframe id=&quot;vs_iframe&quot; src=&quot;https://www.viewstl.com/?embedded&amp;amp;url=https://raw.githubusercontent.com/llmora/deprogramming-obsolescence/master/nikko-hawg/nikko_hawg_steering.stl&amp;amp;color=gray&amp;amp;bgcolor=transparent&amp;amp;noborder=yes&quot; style=&quot;border:0;margin:0;width:100%;height:644px;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;The piece is symmetric, so you can print it for both the right and left sides of the car.&lt;/p&gt;

&lt;p&gt;Considering the shape of the piece I printed it using supports so that the base of the “pipe” rests against the printer bed. While I printed the pieces in PLA it would probably be more sensible to print them in ABS, so they are not as brittle and can withstand the wheels hitting obstacles. I will keep you posted on how I fare.&lt;/p&gt;

&lt;p&gt;Once printed the installation is quite simple. Start by removing the original piece from the car and disassembling it: remove the metal drive shaft that connects the transmission to the wheels and then the two white plastic rings that are attached to each end of the piece.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/nikko_unmounting.png&quot;&gt; &lt;img src=&quot;/images/nikko_unmounting.png&quot; alt=&quot;&quot; title=&quot;Nikko steering transmission components&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After transferring the rings and drive shaft to the newly printed part, install the two M3 nuts inside the nut catchers by sliding them in with your finger (or with the help of small needle-nose pliers), make sure the nuts are centered on the screw holes.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/nikko_nuts.png&quot;&gt; &lt;img src=&quot;/images/nikko_nuts.png&quot; alt=&quot;&quot; title=&quot;Nikko steering transmission nut catcher&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that should be it, the result is a one-to-one replacement of the original piece, but with less concerns for losing screws in the middle of a driving session.&lt;/p&gt;

&lt;div class=&quot;embed-container&quot;&gt;
  &lt;iframe src=&quot;https://www.youtube.com/embed/TO10eFh-q8Y&quot; width=&quot;700&quot; height=&quot;480&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;</content><author><name></name></author><category term="making" /><summary type="html">The Nikko Hawg RC car piece for the steering-transmission is made of ABS plastic and is held in place through two metal screws which eventually eat into the plastic, loosening the connection and preventing the steering from working. I ran out of replacement screws so decided to 3D print a redesigned piece with nut catchers that would not suffer from the same problem, you can find the STL files on our Deprogramming Obsolescence repository (along with editable OpenSCAD files in case you need to adjust the sizes for other similar Nikko cars). If you modify the designs can you share them back with the community please?</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/nikko.jpg" /><media:content medium="image" url="http://flow.gi/nikko.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RSA SecurID hardware token reverse engineering</title><link href="http://flow.gi/SecurIDReverseEngineering/" rel="alternate" type="text/html" title="RSA SecurID hardware token reverse engineering" /><published>2021-07-04T00:00:00+00:00</published><updated>2021-07-04T00:00:00+00:00</updated><id>http://flow.gi/SecurIDReverseEngineering</id><content type="html" xml:base="http://flow.gi/SecurIDReverseEngineering/">&lt;p&gt;Reverse engineering an RSA SecurID hardware token to understand its key operation principles and how resistant they are to attacks to obtain the seed vs the software token option.&lt;/p&gt;

&lt;h1 id=&quot;hardware-tokens-vs-software-tokens&quot;&gt;Hardware tokens vs Software tokens&lt;/h1&gt;

&lt;p&gt;At work we regularly have the discussion on whether we should introduce the convenient two-factor authentication software tokens to replace the physical RSA SecurID hardware tokens that we all carry around. The outcome of the review is nearly always  that software tokens are good and convenient, but they are not as tamper proof / tamper evident as hardware tokens: it does not take much to steal the seed from a general purpose CPU and OS, it can be done without leaving any trace that the seed has been compromised, you do not need physical access to the device - effectively cloning the software token without the user knowledge and completely removing one of the factors.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-softtoken.png&quot;&gt; &lt;img src=&quot;/images/securid-softtoken.png&quot; alt=&quot;&quot; title=&quot;RSA SecurID Software token&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To be fair it is relatively easy to reverse engineer the soft tokens and &lt;a href=&quot;http://securology.blogspot.com/2007/11/soft-tokens-arent-tokens-at-all.html&quot;&gt;point out their flaws&lt;/a&gt;, while on the other side we just take RSA’s word at face value when they say that &lt;a href=&quot;https://www.rsa.com/content/dam/en/data-sheet/rsa-securid-hardware-tokens.pdf&quot;&gt;the hardware tokens are tamper resistant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, is it really that difficult to steal the seed from a SecurID hardware token or are we just relying on security through obscurity? Let’s find out…&lt;/p&gt;

&lt;h1 id=&quot;hardware-token-theory-of-operation&quot;&gt;Hardware token theory of operation&lt;/h1&gt;

&lt;p&gt;Without going too much into detail on how SecurID works, suffice to say that the token and the authentication server are time synchronised and share a secret (the seed). Using the current time and the seed, they use a non-reversible algorithm (older versions used an &lt;a href=&quot;https://seclists.org/bugtraq/2000/Dec/459&quot;&gt;RSA proprietary algorithm&lt;/a&gt; but recent versions have moved to AES) to compute a 6-digit number. This number changes every minute and is not predictable, so it is used by a client to prove to the server that they are in physical posession of the token (the “something you have” factor).&lt;/p&gt;

&lt;p&gt;Based on how the SecurID system operates let’s try to make a first guess on how the various components are implemented. For our analysis we are using the SecurID SID700 token, easily the most widely deployed RSA token.&lt;/p&gt;

&lt;h2 id=&quot;power&quot;&gt;Power&lt;/h2&gt;

&lt;p&gt;RSA tokens are purchased with a certain pre-programmed lifespan of 2 to 5 years: the longer the lifespan the more the token costs. When you receive a new token after purchase it is already showing token codes every minute, as a matter of fact the token does not have an “off” switch or a “show me my token code” button, it is simple and easy to use: the user just looks at the token and types the number they see.&lt;/p&gt;

&lt;p&gt;The tokens are self-contained and are non-rechargeable, the battery that powers the whole system needs to last up to 5 years of continuous operation so this means that the implementation will focus on ultra-low current consumption.&lt;/p&gt;

&lt;h2 id=&quot;time-synchronisation&quot;&gt;Time synchronisation&lt;/h2&gt;

&lt;p&gt;While it is easy to keep synchronisation on the server-side, the tokens are totally self-contained so they must include a really accurate oscillator that does not drift substantially over the 3-5 years of token lifetime. The token will be imprinted with the timestamp when it is initialised and will keep track of the number of oscillations since then. The server will need to know, for each token, the time when it was initialised so it can work in sync.&lt;/p&gt;

&lt;h2 id=&quot;seed-management&quot;&gt;Seed management&lt;/h2&gt;

&lt;p&gt;When you buy a batch of tokens, you also receive a “seed file” which needs to be loaded into the authentication server. There is nothing for you to do to the tokens, e.g. they come preloaded with the seeds and already showing valid numbers, the only thing you need to do is to assign a token to a user, and they are ready to go.&lt;/p&gt;

&lt;p&gt;This means that, at some point in time, a random seed was generated and programmed into the token, the “imprint time” recorded and incorporated into a database at RSA which includes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Token serial&lt;/li&gt;
  &lt;li&gt;Activation timestamp&lt;/li&gt;
  &lt;li&gt;Seed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When somebody purchases a batch, the tokens are shipped from a warehouse and the corresponding database entries are sent to the customer as part of the “seed file”. At this point of time there is no need for RSA to keep a copy of the seeds, however it seems that they actually &lt;em&gt;do&lt;/em&gt; keep the seeds after that as highlighted in the &lt;a href=&quot;https://arstechnica.com/information-technology/2011/06/rsa-finally-comes-clean-securid-is-compromised/&quot;&gt;2011 compromise of the RSA SecurID seed database&lt;/a&gt;. A good example on how retaining information beyond the point it ceases to be useful is a bad practice and just increases your exposure to attacks.&lt;/p&gt;

&lt;p&gt;What would be your guess if you had to venture a reason why they kept a copy of the token seeds?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Updated Dec’2022: Mihai commented on this point (thanks!) and put forward the proposal of RSA keeping a copy of the seeds for customers who lose them, and they may not be too far off: an &lt;a href=&quot;https://www.wired.com/story/the-full-story-of-the-stunning-rsa-hack-can-finally-be-told/&quot;&gt;article published 10 years after the hack&lt;/a&gt; reveals the following nugget of information: “Even after the CD was shipped to a client, those seeds remained on the seed warehouse server as a backup if the customer’s SecurID server or its setup CD were somehow corrupted.”&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;token-manufacture&quot;&gt;Token manufacture&lt;/h2&gt;

&lt;p&gt;Because the token arrives with the seed preloaded it needs to be programmed before it gets to us.&lt;/p&gt;

&lt;p&gt;The serial number and expiration date of the token are printed on a piece of rubber on the back of the token that is easily removed to expose 7 circuit pads, an expensive arrangement if these pads are used just for testing as part of the manufacturer quality assurance process. Instead we believe that these pads are used to program the token with the seed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-pads.jpg&quot;&gt; &lt;img src=&quot;/images/securid-pads.jpg&quot; alt=&quot;&quot; title=&quot;RSA SecurID back pads&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This makes sense as the act of programming the seed into the token requires access to the secrets database to retrieve the serial and seed that the token needs to use, and to record the activation timestamp. It is likely that RSA would want to separate token manufacture from token “imprinting” given the very different security requirements for both processes.&lt;/p&gt;

&lt;p&gt;With this in mind we believe that the manufacturing process would be split in two steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Assembly&lt;/strong&gt;: Assemble the circuit and the physical token, likely in a standard fab facility. This step will include manufacturing the PCB, assembling the components, installing the battery and sealing the token. At this stage the token will be running without a seed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Imprinting&lt;/strong&gt;: Ship the tokens to a secure RSA facility where the token seeds are programmed using the 7 circuit pads and the serial number plus expiration dates are printed on the rubber cover and the imprinting time recorded in the central SecurID database.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only sensitive component that would be exposed in step #1 would be the algorithm that would run inside the token, the microcontroller will probably have protections against extractions of the code - and in any event the algorithm is implemented in the soft tokens nowadays, so hardly a secret.&lt;/p&gt;

&lt;p&gt;The cost of maintaining security in these separate scenarios would come at the expense of manufacturing efficiency, adding queueing between both facilities which probably explains the world-wide shortage of RSA tokens during COVID-19 due to the increased demand. You can easily ramp up assembly by using additional fabs, but the bottleneck would be the imprinting process as we assume there is a limited number of “imprinting” facilities.&lt;/p&gt;

&lt;h1 id=&quot;attacking-the-token&quot;&gt;Attacking the token&lt;/h1&gt;

&lt;h2 id=&quot;hypothesis-1-dump-seed-from-eeprom&quot;&gt;Hypothesis #1: Dump seed from EEPROM&lt;/h2&gt;

&lt;p&gt;Because the seed needs to be programmed at the time of imprinting, it needs to be stored somewhere where the microcontroller can access it, likely to be an EEPROM, maybe external to the microcontroller. It is possible that the data is somehow encrypted, but it will be great if we can extract it from the memory using something like &lt;a href=&quot;/eepeep/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt;&lt;/a&gt;. This is probably a naive assumption as the strength of the device depends on the security of the seed, but then again if we do not test the hypothesis we will never know.&lt;/p&gt;

&lt;p&gt;To find out the location of the memory used to store the seed we need to disassemble the token and identify the components that make up the circuit - I used an expired token from my online banking (if you do not have one laying around you can get a batch of expired tokens off eBay).&lt;/p&gt;

&lt;p&gt;The external case of the token comes out quite easily, we just apply some force in the hole that the keyring holder is attached to and extract the circuit that is sandwiched between the top and bottom covers:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-disassemble-mechanical.png&quot;&gt; &lt;img src=&quot;/images/securid-disassemble-mechanical.png&quot; alt=&quot;&quot; title=&quot;RSA SecurID back pads&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that it uses a CR2032 3V battery to power the whole circuit, and the only visible areas are the 7 programming pads on one side (along with a few test pins and vias) and the LCD display on the other. The whole circuit is enclosed in semi-rigid transparent resin potting to waterproof the token. The removal of this resin is a matter of patience and exacto-knife and tweezers work, slowly cut into the resin and make sure you do not damage any of the components, then pull away chunks of resin using the tweezers.&lt;/p&gt;

&lt;p&gt;You will soon run out of resin and be left with the LCD display that hides the actual circuit so you can try to de-solder it or to pry it away with a screwdriver and a bit more of patience. With the LCD display removed we can see a few components, continue removing the resin (really carefully) until you get as much a clean as PCB as you can, something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-scrapped.png&quot;&gt; &lt;img src=&quot;/images/securid-scrapped.png&quot; alt=&quot;&quot; title=&quot;RSA SecurID scrapped&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a couple of interesting markings on the PCB:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;P/N 430P000012 REV A4&lt;/strong&gt;, the part number and revision of this token design&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;E187451&lt;/strong&gt;, a &lt;a href=&quot;https://iq.ul.com/pwb/Trade.aspx&quot;&gt;UL certificate&lt;/a&gt; for &lt;a href=&quot;http://www.leadjump.com/&quot;&gt;LEAD JUMP PCB (ZHUHAI) LTD&lt;/a&gt;, likely the fab used for the assembly of the token during the manufacturing process. The logo on the PCB also matches that of Lead Jump.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that is about it, apart from a bunch of capacitors and resistors there appears to be only two active components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;An SMD oscillator&lt;/strong&gt;: likely a 32.768KHz clock as used in real-time clock applications - markings on my oscillator are CB701, no datasheet identified.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;An epoxy blob&lt;/strong&gt; which seems to cover the MCU that drives the token.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-pcb-markings.png&quot;&gt; &lt;img src=&quot;/images/securid-pcb-markings.png&quot; alt=&quot;&quot; title=&quot;PCB components&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No external EEPROM or any kind of memory, so the storage must be embedded as part of the MCU - no way to dump it using traditional methods, so let’s think of something else.&lt;/p&gt;

&lt;h2 id=&quot;hypothesis-2-access-memory-using-mcu-pin-functionality&quot;&gt;Hypothesis #2: Access memory using MCU pin functionality&lt;/h2&gt;

&lt;p&gt;For this attack attempt we need to understand the MCU architecture underneath the blob. Considering that RSA has manufactured close to 100 million of these devices and that the security of the device depends on the protection of the seed I expect this will be a custom die.&lt;/p&gt;

&lt;p&gt;I do not have the lab equipment to remove the epoxy, x-ray the blob or de-layer the MCU so I looked for previous work undertaken in this area. I found the work by &lt;a href=&quot;https://www.flickr.com/photos/travisgoodspeed/5596181674/&quot;&gt;Travis Goodspeed&lt;/a&gt; who had exposed the MCU and done an initial analysis of the chip, however the resolution of the die images was too low so I could not look at the detail. Thanks to the contribution of &lt;a href=&quot;http://arcadehacker.blogspot.com/&quot;&gt;Eduardo Cruz&lt;/a&gt; he located &lt;a href=&quot;https://siliconpr0n.org/map/rsa/securid-1c573718a-1p95/&quot;&gt;high-resolution images of the MCU&lt;/a&gt; put together by &lt;a href=&quot;https://twitter.com/johndmcmaster&quot;&gt;John McMaster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking through the images I found work already done on reverse engineering the MCU in the &lt;a href=&quot;https://siliconpr0n.org/archive/doku.php?id=rsa:securid:start&quot;&gt;siliconpr0n archives&lt;/a&gt; a few years ago. Going through the material I saw that they had identified a marking in the die “1C573718A” and that led them to the &lt;a href=&quot;/assets/files/securid-LC573718A.pdf&quot;&gt;Sanyo LC573718A MCU&lt;/a&gt;, a general-purpose 4-bit MCU with some very familiar specs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Built-in LCD drivers to drive up to 120 segments (the SID700 SecurID token has a total of 49 LCD segments)&lt;/li&gt;
  &lt;li&gt;Operates at 2.4V-3.6V&lt;/li&gt;
  &lt;li&gt;Very low power consumption&lt;/li&gt;
  &lt;li&gt;ROM: 8192 x 8 bits&lt;/li&gt;
  &lt;li&gt;RAM: 512 x 4 bits&lt;/li&gt;
  &lt;li&gt;Chronograph function with 100ms resolution&lt;/li&gt;
  &lt;li&gt;Requires an external 32.768KHz oscillator and a bunch of capacitors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Funnily enough this MCU was originally created to run LCD games (it has a built-in buzzer port for instance). I could not find any references, but it will not be surprising if you found one powering a few Sanyo &lt;a href=&quot;http://www.liquidcrystal.co.nz/watches/sanyo-v-space-wars/&quot;&gt;game watches&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-gamewatch.png&quot;&gt; &lt;img src=&quot;/images/securid-gamewatch.png&quot; alt=&quot;&quot; title=&quot;SecurID gamewatches :-)&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this stage it is impossible to know if Sanyo customised the MCU for RSA, however the pinout matches 100% that of the chip images (left image from Sanyo datasheet, right image SecurID die from siliconpr0n &lt;a href=&quot;https://siliconpr0n.org/archive/doku.php?id=rsa:securid:1c&quot;&gt;[credits]&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-mcu-pinout.png&quot;&gt; &lt;img src=&quot;/images/securid-mcu-pinout.png&quot; alt=&quot;&quot; title=&quot;MCU pinout&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The datasheet is not very extensive, but it covers the pinout, which can be summarised as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEG1-SEG32 + COM1-COM4&lt;/code&gt;: LCD segment connections&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VDD1, VDD2, VDD3, VDDX, VSS, VSSX&lt;/code&gt;: Power connections&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XT1, XT2&lt;/code&gt;: Oscillator input&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CUP1-CUP2&lt;/code&gt;: Capacitor pins for step-up, step-down (whatever this may be)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;P00, P01, P02, P03&lt;/code&gt;: 4-bit I/O port&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;P10, P11&lt;/code&gt;: 2-bit I/O port&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;M1, M2&lt;/code&gt;: 2-bit input port&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;S1, S2, S3, S4&lt;/code&gt;: 4-bit input port&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ALM&lt;/code&gt;: Buzzer output (it would be nice to have a bleeping fob)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RES&lt;/code&gt;: Reset&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T3, TST, HZ32&lt;/code&gt;: Test pins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we can see there are no references to pins to read/write from memory (or for in-system programming), so either the MCU does not have that capability to access and program memory or it is not documented on the datasheet.&lt;/p&gt;

&lt;p&gt;A closer look at the MCU specs shows why there is no functionality to write into the MCU memory: &lt;strong&gt;the ROM is actually masked ROM which is written during the die manufacturing process by Sanyo, following RSA specs, and that cannot be modified at run-time&lt;/strong&gt;. This means that all the tokens share the same ROM contents: static data and program code for the MCU but not the seed - as by the time the imprinting process happens the ROM can no longer be modified.&lt;/p&gt;

&lt;p&gt;The only location where the seed can be stored is the MCU SRAM (!), a location that is wiped whenever the MCU loses power. But because the token is designed to constantly run on battery power the RAM is never wiped and the MCU holds the values stored there. If at any point the MCU loses power, the token loses the seed and it goes back to a pre-imprinting state (where the fob flashes with the “888888” message):&lt;/p&gt;

&lt;div class=&quot;embed-container&quot;&gt;
  &lt;iframe src=&quot;https://www.youtube.com/embed/zA2qRyIm9O8&quot; width=&quot;700&quot; height=&quot;480&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;(While I was trying to free the old battery I managed to break the LCD, so only the unbroken segments flash - but you get the idea)&lt;/p&gt;

&lt;p&gt;Because the SRAM is only accessible internally by the MCU there are no pins that expose its contents, so this attack cannot be progressed any more and the hypothesis needs to be abandoned.&lt;/p&gt;

&lt;h2 id=&quot;hypothesis-3-extract-the-key-from-sram-using-die-analysis&quot;&gt;Hypothesis #3: Extract the key from SRAM using die analysis&lt;/h2&gt;

&lt;p&gt;If the seed is stored in SRAM, can we not extract it directly from the die? Data stored in a SRAM is not optically visible, as each SRAM bit looks the same - to write and modify the bit a voltage is applied and a flip-flop is activated to hold the zero or the 1. Potentially and if you could remove the epoxy and chip cover without disconnecting the power supply you could &lt;a href=&quot;http://lamp.tu-graz.ac.at/~hadley/sem/vc/voltagecontrast.php&quot;&gt;use a SEM to measure voltage contrasts&lt;/a&gt; and systematically dump the contents of the SRAM.&lt;/p&gt;

&lt;p&gt;While this sounds plausible this is well beyond my knowledge of electronics, so it is not a hypothesis that I could personally progress. Any comments, views or experiences on this welcome.&lt;/p&gt;

&lt;h2 id=&quot;hypothesis-4-reverse-engineer-the-imprinting-process-and-use-it-to-extract-the-seed-instead-of-storing-it&quot;&gt;Hypothesis #4: Reverse engineer the imprinting process and use it to extract the seed instead of storing it&lt;/h2&gt;

&lt;p&gt;We have seen that the Sanyo MCU does not have any specific pins to access the SRAM, however this is not totally accurate for the RSA fobs: during the imprinting process the seed must be written to the SRAM using the 7 pads on the back of the token, so &lt;em&gt;there are memory access capabilities through these external pads&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see if we can understand what each of the pads do and how they fit together to write the seed to the token - and then find out if the process can be used to read the seed back.&lt;/p&gt;

&lt;p&gt;Quite a few hours of tracing with a multimeter show the following connections for the seven pad connectors (labelled from left to right):&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/securid-pads-numbered.jpg&quot;&gt; &lt;img src=&quot;/images/securid-pads-numbered.jpg&quot; alt=&quot;&quot; title=&quot;RSA SecurID back pads&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T1&lt;/code&gt;: Ground?&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T2&lt;/code&gt;: Power?&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T3&lt;/code&gt;: Power / Reset?&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T4-T7&lt;/code&gt;: These four pins are connected through a 2.2k resistor network to the MCU. Because the connections go under the blob it is not possible to trace them easily, but judging from the pin positions and some of the &lt;a href=&quot;https://siliconpr0n.org/map/rsa/securid-1c573718a-1p95/mz_ns50xu/&quot;&gt;MCU image pin usage&lt;/a&gt; these are likely to be connected to the M1, M2 (input only), P10 and P11 (input and output) ports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are GPIO pins whose functionality needs to be implemented in the ROM firmware by the developer of the particular application, in this case RSA. This means that the imprinting process does not use MCU native ports, but that the imprinting functionality is implemented in the firmware, so in order to understand what the pins do (and how the imprinting protocol works) we need to reverse engineer the firmware first.&lt;/p&gt;

&lt;h3 id=&quot;extracting-the-rom-using-image-processing&quot;&gt;Extracting the ROM using image processing&lt;/h3&gt;

&lt;p&gt;We go back to the previous work done by siliconpr0n who have &lt;a href=&quot;https://siliconpr0n.org/map/rsa/securid-2c/rom_mit100x/&quot;&gt;really close-up images of the SecurID MCU&lt;/a&gt; available. As opposed to SRAM the mask ROM is static so it can be pre-programmed using fixed logical gate arrangements to store a zero or a one. There are &lt;a href=&quot;https://siliconpr0n.org/wiki/doku.php?id=rom:mask&quot;&gt;various technologies for mask ROMs&lt;/a&gt; however with the appropriate decapping and staining it is possible to visually recognise when a zero or a one is stored on each memory position.&lt;/p&gt;

&lt;p&gt;From the images &lt;a href=&quot;https://siliconpr0n.org/archive/lib/exe/detail.php?id=rsa%3Asecurid%3A1c&amp;amp;media=rsa:securid:black:overview-sectors.jpg&quot;&gt;we can see&lt;/a&gt; that there are 8KB of ROM organised in two banks. Each bank has 128 rows and each rows is 16 columns with 16 bits in each column, totalling 65536 bits (8 KB).&lt;/p&gt;

&lt;p&gt;Using the images linked above and &lt;a href=&quot;https://github.com/AdamLaurie/rompar&quot;&gt;rompar&lt;/a&gt;, a masked ROM optical data extraction tool which takes an image of the masked ROM and produces the binary representation of the zeros and ones stored in it we were able to obtain the &lt;a href=&quot;/assets/files/securid-dump.txt&quot;&gt;8KB of the ROM that stores the configuration and program for the RSA token&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;rom-structure&quot;&gt;ROM structure&lt;/h3&gt;

&lt;p&gt;Now that we have the dump we need to try and make heads or tails of it, so that we can obtain the program running on the MCU. The main problem is understanding how the ROM is laid out, what part is the IC configuration and which part is the MCU program, so we can disassemble it.&lt;/p&gt;

&lt;p&gt;At this point of time we would use the MCU documentation and development toolkit to compile a sample program, see how the data is stored and try to find patterns to understand how the masked ROM is created, however it seems that the age of this MCU coupled with the fact that Sanyo semiconductors has been resold multiple times leaves us with very little information and no working toolset to explore how the memory is laid down (if you happen to have access to such a toolkit drop me a line please).&lt;/p&gt;

&lt;p&gt;During my research I have come across the following nuggets of information:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;From the datasheets of the &lt;a href=&quot;/assets/files/LC5734_SanyoSemiconDevice_with_instruction_opcodes.pdf&quot;&gt;LC5734 family&lt;/a&gt;, &lt;a href=&quot;/assets/files/LC573104A_02A_4144_with_instruction_opcodes.pdf&quot;&gt;LC573104A&lt;/a&gt; and &lt;a href=&quot;/assets/files/LC573406A_SanyoSemiconDevice_with_instruction_opcodes.pdf&quot;&gt;LC573406A&lt;/a&gt; we have been able to extract the opcodes for the various instructions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;From the &lt;a href=&quot;/assets/files/LC65E1104_SanyoSemiconDevice_EEPROM.pdf&quot;&gt;similar LC65E1104_datasheet&lt;/a&gt;:
    &lt;ul&gt;
      &lt;li&gt;Part of the mask ROM is the IC configuration, e.g. type of oscillator, watchdog reset, port output levels, etc. .&lt;/li&gt;
      &lt;li&gt;The option data is stored between 0x1000 and 0x100a (10 bits)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;From the &lt;a href=&quot;/assets/files/LC5862H_with_EEPROM.pdf&quot;&gt;LC5862H datasheet&lt;/a&gt;:
    &lt;ul&gt;
      &lt;li&gt;The option data is written to the end of the EPROM at 0x4000 to 0x40FF in blocks of 8 bits (256 bits)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spent a considerable amount of time looking at the dump and the images to try and reverse-engineer the memory access method but it is just not my area of expertise. Looking at different permutations of the memory dump, coupled with a hand-made disassembler for the Sanyo MCUs did not bear any results so it seems like the memory layout is not straight-forward, the data is somehow encoded (e.g. RSA customised the chip to encode the instructions in the ROM, or the opcodes differ for this particular MCU). Considering that there are areas of memory with contiguous 1s or 0s, it does not seem like the encoding/encryption hypothesis holds up.&lt;/p&gt;

&lt;h3 id=&quot;completing-the-work&quot;&gt;Completing the work&lt;/h3&gt;

&lt;p&gt;If you are successful in identifying the memory layout let me know, it should be a breeze afterwards to get the software decompiled and the imprinting process identified - as it stands though I need to abandon this avenue of work until there is a breakthrough in this area which will allow us to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Document how the imprinting process works&lt;/li&gt;
  &lt;li&gt;Identify the SRAM addresses where the seed is written to&lt;/li&gt;
  &lt;li&gt;Search for loopholes in the imprinting process to read back data from SRAM&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;We set ourselves to prove whether the hardware token seeds are substantially harder to compromise that soft tokens and, while there are still avenues for attack opened, it seems that the trust we put in hardware tokens is well-placed and that the design, although very surprising (seed stored in volatile RAM, non-dedicated IC), does hold itself to more than a casual effort at reverse engineering.&lt;/p&gt;</content><author><name></name></author><category term="security" /><category term="making" /><summary type="html">Reverse engineering an RSA SecurID hardware token to understand its key operation principles and how resistant they are to attacks to obtain the seed vs the software token option.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/securid.png" /><media:content medium="image" url="http://flow.gi/securid.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">eepeep: Dumping an in-circuit EEPROM</title><link href="http://flow.gi/eepeep/" rel="alternate" type="text/html" title="eepeep: Dumping an in-circuit EEPROM" /><published>2020-07-20T00:00:00+00:00</published><updated>2020-07-20T00:00:00+00:00</updated><id>http://flow.gi/eepeep</id><content type="html" xml:base="http://flow.gi/eepeep/">&lt;p&gt;While trying to reverse engineer the VidCon Youtube OnStage bracelets I had the need to access the contents of an EEPROM that is used as external storage by the main microcontroller, however I did not want to de-solder the EEPROM to get access to it so I started looking at ways to dump the contents while the EEPROM was still in-circuit.&lt;/p&gt;

&lt;p&gt;I found a couple of articles (&lt;a href=&quot;http://chrisgreenley.com/projects/eeprom-dumping/&quot;&gt;1&lt;/a&gt;,&lt;a href=&quot;https://learn.sparkfun.com/tutorials/reading-and-writing-serial-eeproms/all&quot;&gt;2&lt;/a&gt;) explaining how to access and dump EEPROMs using an Arduino but the firmware needed to be adapted for each type of EEPROM and re-flashed, and the transfer of the chip contents depended on a generic serial port application so it seemed like coming up with something more generic that could be used for a wide range of EEPROMS could be an interesting project.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt; was developed with the following design principles in mind:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It needs to be open source / open hardware so it is easy for people to modify and extend it&lt;/li&gt;
  &lt;li&gt;It needs to be easy to use (within reason, after all it is designed for reverse-engineers)&lt;/li&gt;
  &lt;li&gt;It must support as many EEPROMs as possible out of the box&lt;/li&gt;
  &lt;li&gt;It needs to be easy for users to contribute new EEPROM details&lt;/li&gt;
  &lt;li&gt;It must, at least, support I&lt;sup&gt;2&lt;/sup&gt;C and SPI protocols&lt;/li&gt;
  &lt;li&gt;The client must be multiplatform and run, at least, on Windows, Linux and MacOS&lt;/li&gt;
  &lt;li&gt;The hardware component must be affordable and easy to implement across multiple platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the code and hardware design in the &lt;a href=&quot;https://github.com/llmora/eepeep/&quot;&gt;project repository&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;setting-up-your-equipment&quot;&gt;Setting up your equipment&lt;/h1&gt;

&lt;p&gt;Start by &lt;a href=&quot;https://github.com/llmora/eepeep/#software-component&quot;&gt;downloading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt;&lt;/a&gt;, v1.0 was just released - it has been tested on a range of EEPROMs, however your mileage may vary. Use with caution and do not use in any circuit you would mind going up in smoke, and please &lt;a href=&quot;https://github.com/llmora/eepeep/issues&quot;&gt;file any issues&lt;/a&gt; you encounter with it.&lt;/p&gt;

&lt;p&gt;Put together the hardware component using the following circuit:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/llmora/eepeep/master/server/arduino/arduino-shield_bb.png&quot; title=&quot;hardware circuit&quot;&gt; &lt;img src=&quot;https://raw.githubusercontent.com/llmora/eepeep/master/server/arduino/arduino-shield_bb.png&quot; alt=&quot;hardware circuit&quot; title=&quot;hardware circuit&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The circuit is just a basic setup to allow the Arduino to access the I&lt;sup&gt;2&lt;/sup&gt;C bus that the EEPROM is connected to. The terminals labelled SCL, SDA, GND and 5v/3.3v will be connected to the in-circuit EEPROM, you can find more details in the &lt;a href=&quot;#hardware-design&quot;&gt;Hardware design&lt;/a&gt; section and the detailed bill of materials in the &lt;a href=&quot;https://github.com/llmora/eepeep/&quot;&gt;project repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After assembling the circuit flash the Arduino board with &lt;a href=&quot;https://raw.githubusercontent.com/llmora/eepeep/master/server/arduino/eepeep/eepeep.ino&quot;&gt;this script&lt;/a&gt; and you are ready to go.&lt;/p&gt;

&lt;h1 id=&quot;dumping-an-in-circuit-eeprom&quot;&gt;Dumping an in-circuit EEPROM&lt;/h1&gt;

&lt;p&gt;Start by identifying the EEPROM part number and finding a datasheet for it, this will indicate the key parameters you need to feed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt; to access the data stored in the device.&lt;/p&gt;

&lt;p&gt;This is what you are after:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Protocols supported&lt;/strong&gt;: at the moment &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt; only supports dumping I&lt;sup&gt;2&lt;/sup&gt;C-enabled EEPROMs&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pinout&lt;/strong&gt;: find out the EEPROM pins for SDA, SCL, Vcc and GND.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Voltage level&lt;/strong&gt;: whether it will need a 3.3v or a 5v power line.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Frequency of the I&lt;sup&gt;2&lt;/sup&gt;C bus&lt;/strong&gt;: typically 100KHz or 400KHz&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unique I&lt;sup&gt;2&lt;/sup&gt;C device address&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;EEPROM size&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Through this article we will use the following EEPROM as the target we want to dump:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-24c02s.png&quot;&gt; &lt;img src=&quot;/images/eepeep-24c02s.png&quot; alt=&quot;&quot; title=&quot;24c02s&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the markings we can make out it is a 24C02S, a 2048bits serial EEPROM with a widely available &lt;a href=&quot;/assets/files/eepeep-24c02s.pdf&quot;&gt;datasheet&lt;/a&gt;. We can extract the following details from the datasheet:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Protocols supported&lt;/strong&gt;: it supports I&lt;sup&gt;2&lt;/sup&gt;C&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pinout&lt;/strong&gt;: we can see the pinout for the SOT-23 package in the datasheet, we have annotated the picture above with the pins&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Voltage level&lt;/strong&gt;: the accepted ranges mean it can operate both at 3.3v and 5v. We will stick to 5v.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Frequency of the I&lt;sup&gt;2&lt;/sup&gt;C bus&lt;/strong&gt;: both 100KHz and 400KHz supported&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Unique I&lt;sup&gt;2&lt;/sup&gt;C device address&lt;/strong&gt;: we can see the device can be addressed from 1010000 to 1010111 (e.g. 0x50 to 0x57)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;EEPROM size&lt;/strong&gt;: 2048 bits, e.g. 256 bytes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remove any power source from the target circuit that the EEPROM is connected to (e.g. the one you are reverse engineering) as we will be powering the EEPROM and the I&lt;sup&gt;2&lt;/sup&gt;C bus through the Arduino.&lt;/p&gt;

&lt;p&gt;Using the pinout that you have identified connect it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt; hardware component headers, using logical probes or anything else that works for you. Make sure you select the appropriate voltage header and that the pin connections are accurate, this is the only point where you can destroy the EEPROM (and the rest of the circuit it is attached to).&lt;/p&gt;

&lt;p&gt;I have some small logical probes that are ideal for this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-probes.png&quot;&gt; &lt;img src=&quot;/images/eepeep-probes.png&quot; alt=&quot;&quot; title=&quot;Probes&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-probes-2.png&quot;&gt; &lt;img src=&quot;/images/eepeep-probes-2.png&quot; alt=&quot;&quot; title=&quot;Probes 2&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect the Arduino to the host through USB and launch the software component, choose the serial port the Arduino is connected to and then click on “Connect”.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-serial-selection.png&quot;&gt; &lt;img src=&quot;/images/eepeep-serial-selection.png&quot; alt=&quot;&quot; title=&quot;Serial connection selection&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all goes well you should receive a confirmation that a connection has been established to the software component, and that the hardware is ready. If you cannot see this double-check the firmware flashing was successful.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-connected.png&quot;&gt; &lt;img src=&quot;/images/eepeep-connected.png&quot; alt=&quot;&quot; title=&quot;Successful connection&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the bus frequency obtained from the datasheet; if you do not have this try 100 or 400 KHz as these are the &lt;a href=&quot;http://www.nxp.com/documents/user_manual/UM10204.pdf&quot;&gt;standard I&lt;sup&gt;2&lt;/sup&gt;C bus frequencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click on the “Scan” button to scan the I&lt;sup&gt;2&lt;/sup&gt;C bus for the addresses of all valid connected devices - if your target circuit has multiple devices connected to the bus all of these will show up on the scan results:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-scan.png&quot;&gt; &lt;img src=&quot;/images/eepeep-scan.png&quot; alt=&quot;&quot; title=&quot;Successful connection&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the datasheet to identify which address belongs to your EEPROM module, beware as a device may have multiple addresses. If the scan result comes back empty please double-check the connection between the hardware component and the EEPROM.&lt;/p&gt;

&lt;p&gt;Input the desired device I&lt;sup&gt;2&lt;/sup&gt;C address to dump in the “Address” field and enter the start and end memory addresses to dump, typically these will be zero as the start address and the total memory size as the end address. For instance in the case of the 24c02s the I&lt;sup&gt;2&lt;/sup&gt;C address is between 0x50 and 0x57 and it can hold 256 bytes of data, so we will set the memory end address to dump at 0xFF:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/eepeep-dump.png&quot;&gt; &lt;img src=&quot;/images/eepeep-dump.png&quot; alt=&quot;&quot; title=&quot;Successful connection&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you click on “Dump EEPROM contents” you will see the progress on the console output, after a while (depending on the EEPROM size) a window will pop-up asking where do you want to save the extracted EEPROM contents for further analysis.&lt;/p&gt;

&lt;p&gt;If you use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eepeep&lt;/code&gt; to dump a new EEPROM type we encourage you to share the parameters used for that EEPROM so that others may benefit from your work, just &lt;a href=&quot;https://github.com/llmora/eepeep/issues&quot;&gt;submit an issue&lt;/a&gt; with the EEPROM details so we can add it to a future version of the software.&lt;/p&gt;

&lt;h1 id=&quot;architecture&quot;&gt;Architecture&lt;/h1&gt;

&lt;h2 id=&quot;software-design&quot;&gt;Software design&lt;/h2&gt;

&lt;p&gt;To meet the multiplatform requirement for the software component it has been implemented in electronjs, relying on the &lt;a href=&quot;https://serialport.io/&quot;&gt;serialport&lt;/a&gt; library for communications with the hardware component. The cost of the multiplatform capability is an exagerated memory consumption (after all we are running Chromium inside the application) and a software package size close to 100MB for an application that developed in C may not take more than a few KBs.&lt;/p&gt;

&lt;p&gt;The software has two key components: a serial protocol parser and a state machine that exchanges messages with the hardware component. The serial protocol parser has been implemented like a stream Transform and takes care of the low-level protocol implementation, including reassembly of frames and message passing to the core process. The state machine is implemented using a set of case/switch statements that ensure the client and the hardware components are kept in sync.&lt;/p&gt;

&lt;h2 id=&quot;hardware-design&quot;&gt;Hardware design&lt;/h2&gt;

&lt;p&gt;The hardware component acts as a bridge between the client and the EEPROM, implementing the bus scanning and EEPROM access protocols. At its core it is a state machine that receives messages from the client (scan bus, dump eeprom) and translates these to the I&lt;sup&gt;2&lt;/sup&gt;C protocol commands that interact with the EEPROMs.&lt;/p&gt;

&lt;p&gt;The interaction with the EEPROM happens over the I&lt;sup&gt;2&lt;/sup&gt;C protocol, so the hardware circuit is a &lt;a href=&quot;https://www.ti.com/lit/an/slva689/slva689.pdf&quot;&gt;simple implementation of an I&lt;sup&gt;2&lt;/sup&gt;C access setup&lt;/a&gt; by using a couple of 4.7k pull-up resistors.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://raw.githubusercontent.com/llmora/eepeep/master/server/arduino/eepeep/eepeep.ino&quot;&gt;hardware component&lt;/a&gt; has been designed with simplicity in mind so it can be easily ported to other architectures, the firmware must implement the following functionality:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Command control loop for messages coming from the software client&lt;/li&gt;
  &lt;li&gt;I&lt;sup&gt;2&lt;/sup&gt;C bus scanning&lt;/li&gt;
  &lt;li&gt;I&lt;sup&gt;2&lt;/sup&gt;C memory dump&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="security" /><category term="making" /><summary type="html">While trying to reverse engineer the VidCon Youtube OnStage bracelets I had the need to access the contents of an EEPROM that is used as external storage by the main microcontroller, however I did not want to de-solder the EEPROM to get access to it so I started looking at ways to dump the contents while the EEPROM was still in-circuit.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/eepeep.png" /><media:content medium="image" url="http://flow.gi/eepeep.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">COVID-19 Gibraltar historical statistics</title><link href="http://flow.gi/virus/" rel="alternate" type="text/html" title="COVID-19 Gibraltar historical statistics" /><published>2020-04-03T00:00:00+00:00</published><updated>2020-04-03T00:00:00+00:00</updated><id>http://flow.gi/virus</id><content type="html" xml:base="http://flow.gi/virus/">&lt;p&gt;The Gibraltar Government publishes a daily COVID-19 report that includes number of conducted tests, how many cases are active and how many people have recovered. There is no historical information available so I have recorded the series since these started being published on the 12th March 2020 and generated the charts below. The raw numbers can be &lt;a href=&quot;/assets/js/covid19-gibraltar.js&quot;&gt;downloaded&lt;/a&gt; so you can run your own analysis.&lt;/p&gt;

&lt;div class=&quot;box&quot;&gt;

  &lt;h3&gt;
    Today's update
  &lt;/h3&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    New cases: &lt;span id=&quot;new_cases_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    Active cases: &lt;span id=&quot;active_cases_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    People in isolation: &lt;span id=&quot;isolated_people_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;!--p style=&quot;text-align: center;&quot;&gt;
    Recovered cases: &lt;span id=&quot;recovered_cases_today&quot;&gt;&lt;/span&gt;
  &lt;/p--&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    Deaths: &lt;span id=&quot;deceased_cases_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    People with a single dose: &lt;span id=&quot;first_doses_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    People with two doses: &lt;span id=&quot;second_doses_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    People with three doses: &lt;span id=&quot;third_doses_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    People with full dosage and 1st booster: &lt;span id=&quot;booster_doses_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;p style=&quot;text-align: center;&quot;&gt;
    Total doses administered: &lt;span id=&quot;total_doses_today&quot;&gt;&lt;/span&gt;
  &lt;/p&gt;

  &lt;!--p style=&quot;text-align: center;&quot;&gt;
    Vaccines left in stock: &lt;span id=&quot;stock_doses_today&quot;&gt;&lt;/span&gt;
  &lt;/p--&gt;

&lt;p style=&quot;text-align: right; margin: 5px;&quot;&gt;
  &lt;small&gt;&lt;i&gt;Statistics updated on &lt;span id=&quot;daily_update_ago&quot;&gt;&lt;/span&gt;&lt;/i&gt;&lt;/small&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Note that on the 31st August the Gibraltar Government decided to stop reporting on non-resident cases identified in Gibraltar, so as of that date there are no longer accurate numbers for overall cases detected in Gibraltar. We continue reporting on the total number of cases identified in Gibraltar by using the difference between detected cases and resolved cases.&lt;/p&gt;

&lt;p&gt;Last updated: &lt;em&gt;&lt;span id=&quot;text_last_update&quot;&gt;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
  Tests conducted: &lt;span id=&quot;text_population_tested&quot;&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
  Population confirmed as infected after testing: &lt;span id=&quot;text_population_infected&quot;&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
  Recovery rate: &lt;b&gt;&lt;span id=&quot;text_recovery_rate&quot;&gt;&lt;/span&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;
  Mortality rate: &lt;b&gt;&lt;span id=&quot;text_mortality_rate&quot;&gt;&lt;/span&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;link href=&quot;http://flow.gi/assets/js/apexcharts/styles.css&quot; rel=&quot;stylesheet&quot; /&gt;

&lt;script src=&quot;http://flow.gi/assets/js/apexcharts/apexcharts.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;http://flow.gi/assets/js/covid19-gibraltar.js&quot;&gt;&lt;/script&gt;

&lt;style&gt;
      
    .chart {
      max-width: 800px;
      margin: 35px auto;
    }
      
  &lt;/style&gt;

&lt;div id=&quot;chart_active_recovered&quot; class=&quot;chart&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;chart_newcases&quot; class=&quot;chart&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;chart_vaccination&quot; class=&quot;chart&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;chart_tests&quot; class=&quot;chart&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;chart_total&quot; class=&quot;chart&quot;&gt;&lt;/div&gt;

&lt;form name=&quot;scale&quot;&gt;
    &lt;center&gt;Scale: &lt;input type=&quot;radio&quot; name=&quot;linear&quot; /&gt;&lt;label for=&quot;linear&quot;&gt;Linear&lt;/label&gt;&lt;input type=&quot;radio&quot; name=&quot;logarithmic&quot; /&gt;&lt;label for=&quot;logarithmic&quot;&gt;Logarithmic&lt;/label&gt;&lt;/center&gt;
  &lt;/form&gt;

&lt;p&gt;Source: &lt;a href=&quot;https://www.gibraltar.gov.gi/covid19&quot;&gt;HM Government of Gibraltar&lt;/a&gt;&lt;/p&gt;

&lt;script&gt;

// Constants

var population = 32116;
var sample_tests_positive = 10;
var sample_tests_received = 400;

// Calculate and show key KPIs

var current_active = series.covid19GibraltarSeries.active.slice(-1)[0];
var current_recovered = series.covid19GibraltarSeries.recovered.slice(-1)[0];
var current_isolated = series.covid19GibraltarSeries.selfisolated.slice(-1)[0];
var current_total = current_active + current_recovered;
var current_deceased = series.covid19GibraltarSeries.deceased.slice(-1)[0];
var current_tests_pending = series.covid19GibraltarSeries.tests_pending.slice(-1)[0];
var current_tests_received = series.covid19GibraltarSeries.tests_received.slice(-1)[0];
var current_tests_total = current_tests_pending + current_tests_received;
const current_last_update = new Date(Date.parse(series.covid19GibraltarSeries.dates.slice(-1)[0]));

var population_tested = (current_tests_total / population) * 100;
var population_infected = (current_total / population) * 100;
var recovery_rate = (current_recovered / (current_active + current_recovered + current_deceased)) * 100;
var mortality_rate = parseFloat(current_deceased * 100 / current_total);

var prevalence_rate = (sample_tests_positive / sample_tests_received) * 100;
var prevalence_infected = parseInt((prevalence_rate * population ) / 100, 10);

// Update summary statistics

document.getElementById(&quot;text_population_tested&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_tests_total.toLocaleString() + &quot; (&quot; + population_tested.toFixed(2) + &quot;% of population)&lt;/b&gt; of which &quot; + current_tests_received.toLocaleString() + &quot; have been received&quot;;
document.getElementById(&quot;text_population_infected&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_total.toLocaleString() + &quot; (&quot; + population_infected.toFixed(2) + &quot;%)&lt;/b&gt; of which &quot; + current_active.toLocaleString() + &quot; remain active&quot;;
document.getElementById(&quot;text_recovery_rate&quot;).innerHTML = recovery_rate.toFixed(2) + &quot;%&quot;;
document.getElementById(&quot;text_mortality_rate&quot;).innerHTML = mortality_rate.toFixed(2) + &quot;%&quot;;

prevalence_rate.toFixed(2) + &quot;%&quot;;
document.getElementById(&quot;text_last_update&quot;).innerHTML = current_last_update.toDateString();

// Support functions

function formatChange(input, positive_color, negative_color) {
  var ret = &quot;&quot;

  if (input &gt; 0) {
    ret = `&lt;span class=&quot;${positive_color}&quot;&gt;&lt;i class=&quot;fas fa-sort-up&quot;&gt;&lt;/i&gt;${input}&lt;/span&gt;`
  } else if (input &lt; 0) {
    ret = `&lt;span class=&quot;${negative_color}&quot;&gt;&lt;i class=&quot;fas fa-sort-down&quot;&gt;&lt;/i&gt;${Math.abs(input)}&lt;/span&gt;`
  } else {
    ret = '-'
  }

  return ret
}

function formatElapsedTime(seconds) {
  var ret = `${seconds} seconds`

  if (seconds &lt; 60) {
    var value = seconds

    if (value == 1) {
    ret = `${value} second`
    } else {
    ret = `${value} seconds`
    }

  } else if (seconds &lt; (60 * 60)) {
    var value = Math.floor(seconds / 60)

    if (value == 1) {
    ret = `${value} minute`
    } else {
    ret = `${value} minutes`
    }

  } else if (seconds &lt; (60 * 60 * 24)) {
    var value = Math.floor(seconds / (60 * 60))

    if (value == 1) {
    ret = `${value} hour`
    } else {
    ret = `${value} hours`
    }

  } else {
    var value = Math.floor(seconds / (60 * 60 * 24))

    if (value == 1) {
    ret = `${value} day`
    } else {
    ret = `${value} days`
    }
  }

  return `${ret} ago`
}

function getLastValidElement(array, offset) {

  var index;

  ret = 0

  for (index = offset; index &lt; array.length; index++) {
    ret = array.slice(-1 * index)[0]

    if (ret != null) {
      return ret
    }
  }

  return ret
}

// Calculate new and total cases series

function average(values) {
  var sum = 0;

  for(var i = 0; i &lt; values.length; i++) {
    sum = sum + values[i];
  }

  return values.length? sum / values.length : 0;
}

var newcases = [];
var newcases_avg7d = [];
var totalcases = [];
var totaldoses = [];
var people = [];

var previous_day_infected = 0;
var previous_day_doses = 0;

var current_avg7d = 0;

for(var i = 0; i &lt; series.covid19GibraltarSeries.active.length; i++) {
  current_infected = series.covid19GibraltarSeries.active[i] + series.covid19GibraltarSeries.recovered[i] + series.covid19GibraltarSeries.deceased[i];

  newcases[i] = current_infected - previous_day_infected;
  totalcases[i] = current_infected;
  totaldoses[i] = previous_day_doses + series.covid19GibraltarSeries.doses_shipments[i]
  people[i] = population

  current_avg7d = Math.round(average(newcases.slice(-7)))
  newcases_avg7d[i] = current_avg7d;

  previous_day_infected = current_infected
  previous_day_doses = totaldoses[i]
  
}

series.covid19GibraltarSeries.newcases = newcases;
series.covid19GibraltarSeries.newcases_avg7d = newcases_avg7d;
series.covid19GibraltarSeries.totalcases = totalcases;
series.covid19GibraltarSeries.totaldoses = totaldoses;
series.covid19GibraltarSeries.population = people;

// Update today statistics

/*
var current_new = series.covid19GibraltarSeries.newcases.slice(-1)[0];
var current_first = series.covid19GibraltarSeries.doses_first.slice(-1)[0];
var current_second = series.covid19GibraltarSeries.doses_second.slice(-1)[0];
var current_totaldoses = current_first + current_second;
var current_stock = series.covid19GibraltarSeries.totaldoses.slice(-1)[0] - (current_totaldoses);

var yesterday_active = series.covid19GibraltarSeries.active.slice(-2)[0];
var yesterday_new = series.covid19GibraltarSeries.newcases.slice(-2)[0];
var yesterday_first = series.covid19GibraltarSeries.doses_first.slice(-2)[0];
var yesterday_second = series.covid19GibraltarSeries.doses_second.slice(-2)[0];
var yesterday_totaldoses = yesterday_first + yesterday_second;
var yesterday_stock = series.covid19GibraltarSeries.totaldoses.slice(-2)[0] - (yesterday_totaldoses);

var yesterday_recovered = series.covid19GibraltarSeries.recovered.slice(-2)[0];
var yesterday_total = yesterday_active + yesterday_recovered;
var yesterday_deceased = series.covid19GibraltarSeries.deceased.slice(-2)[0];
var yesterday_tests_pending = series.covid19GibraltarSeries.tests_pending.slice(-2)[0];
var yesterday_tests_received = series.covid19GibraltarSeries.tests_received.slice(-2)[0];
var yesterday_tests_total = yesterday_tests_pending + yesterday_tests_received;
*/

var current_new = getLastValidElement(series.covid19GibraltarSeries.newcases, 1);
var current_first = getLastValidElement(series.covid19GibraltarSeries.doses_first, 1);
var current_second = getLastValidElement(series.covid19GibraltarSeries.doses_second, 1);
var current_third = getLastValidElement(series.covid19GibraltarSeries.doses_third, 1);
var current_booster = getLastValidElement(series.covid19GibraltarSeries.doses_booster, 1);
var current_totaldoses = current_first + current_second + current_third + current_booster;
var current_stock = getLastValidElement(series.covid19GibraltarSeries.totaldoses, 1) - current_totaldoses;

var yesterday_active = getLastValidElement(series.covid19GibraltarSeries.active, 2);
var yesterday_new = getLastValidElement(series.covid19GibraltarSeries.newcases, 2);
var yesterday_first = getLastValidElement(series.covid19GibraltarSeries.doses_first, 2);
var yesterday_second = getLastValidElement(series.covid19GibraltarSeries.doses_second, 2);
var yesterday_third = getLastValidElement(series.covid19GibraltarSeries.doses_third, 2);
var yesterday_booster = getLastValidElement(series.covid19GibraltarSeries.doses_booster, 2);
var yesterday_totaldoses = yesterday_first + yesterday_second + yesterday_third + yesterday_booster;
var yesterday_stock = getLastValidElement(series.covid19GibraltarSeries.totaldoses, 2) - yesterday_totaldoses;

var yesterday_recovered = getLastValidElement(series.covid19GibraltarSeries.recovered, 2);
var yesterday_isolated = getLastValidElement(series.covid19GibraltarSeries.selfisolated, 2);
var yesterday_total = yesterday_active + yesterday_recovered;
var yesterday_deceased = getLastValidElement(series.covid19GibraltarSeries.deceased, 2);
var yesterday_tests_pending = getLastValidElement(series.covid19GibraltarSeries.tests_pending, 2);
var yesterday_tests_received = getLastValidElement(series.covid19GibraltarSeries.tests_received, 2);
var yesterday_tests_total = yesterday_tests_pending + yesterday_tests_received;

var active_cases_change = formatChange(current_active - yesterday_active, &quot;red&quot;, &quot;green&quot;)
var recovered_cases_change = formatChange(current_recovered - yesterday_recovered, &quot;green&quot;, &quot;red&quot;)
var deceased_cases_change = formatChange(current_deceased - yesterday_deceased, &quot;red&quot;, &quot;green&quot;)
var new_cases_change = formatChange(current_new - yesterday_new, &quot;red&quot;, &quot;green&quot;)
var isolated_change = formatChange(current_isolated - yesterday_isolated, &quot;red&quot;, &quot;green&quot;)
var first_doses_change = formatChange(current_first - yesterday_first, &quot;green&quot;, &quot;red&quot;)
var second_doses_change = formatChange(current_second - yesterday_second, &quot;green&quot;, &quot;red&quot;)
var third_doses_change = formatChange(current_third - yesterday_third, &quot;green&quot;, &quot;red&quot;)
var booster_doses_change = formatChange(current_booster - yesterday_booster, &quot;green&quot;, &quot;red&quot;)
var total_doses_change = formatChange(current_totaldoses - yesterday_totaldoses, &quot;green&quot;, &quot;red&quot;)
var stock_doses_change = formatChange(current_stock - yesterday_stock, &quot;green&quot;, &quot;red&quot;)

const updated_seconds_ago = (new Date() - new Date(Date.parse(series.covid19GibraltarSeries.dates.slice(-1)[0]))) / 1000;

document.getElementById(&quot;daily_update_ago&quot;).innerHTML = current_last_update.toDateString() // formatElapsedTime(updated_seconds_ago);

document.getElementById(&quot;active_cases_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_active.toLocaleString() + &quot;&lt;/b&gt; (&quot; + active_cases_change + &quot;)&quot;
// document.getElementById(&quot;recovered_cases_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_recovered.toLocaleString() + &quot;&lt;/b&gt; (&quot; + recovered_cases_change + &quot;)&quot;
document.getElementById(&quot;deceased_cases_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_deceased.toLocaleString() + &quot;&lt;/b&gt; (&quot; + deceased_cases_change + &quot;)&quot;
document.getElementById(&quot;new_cases_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_new.toLocaleString() + &quot;&lt;/b&gt; (&quot; + new_cases_change + &quot;)&quot;
document.getElementById(&quot;first_doses_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_first.toLocaleString() + &quot;&lt;/b&gt; (&quot; + first_doses_change + &quot;)&quot;
document.getElementById(&quot;second_doses_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_second.toLocaleString() + &quot;&lt;/b&gt; (&quot; + second_doses_change + &quot;)&quot;
document.getElementById(&quot;third_doses_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_third.toLocaleString() + &quot;&lt;/b&gt; (&quot; + third_doses_change + &quot;)&quot;
document.getElementById(&quot;booster_doses_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_booster.toLocaleString() + &quot;&lt;/b&gt; (&quot; + booster_doses_change + &quot;)&quot;
document.getElementById(&quot;total_doses_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_totaldoses.toLocaleString() + &quot;&lt;/b&gt; (&quot; + total_doses_change + &quot;)&quot;
// document.getElementById(&quot;stock_doses_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_stock.toLocaleString() + &quot;&lt;/b&gt; (&quot; + stock_doses_change + &quot;)&quot;
document.getElementById(&quot;isolated_people_today&quot;).innerHTML = &quot;&lt;b&gt;&quot; + current_isolated.toLocaleString() + &quot;&lt;/b&gt; (&quot; + isolated_change + &quot;)&quot;


// Configure and show charts

var options_active_recovered = {
  series: [{
      data: series.covid19GibraltarSeries.active,
      name: &quot;Active&quot;
    },
    {
      data: series.covid19GibraltarSeries.recovered,
      name: &quot;Recovered&quot;
    },
    {
      data: series.covid19GibraltarSeries.deceased,
      name: &quot;Deceased&quot;
    },
    {
      data: series.covid19GibraltarSeries.newcases,
      name: &quot;Daily new cases&quot;,
      type: 'column'
    }

  ],
  chart: {
    height: 350,
    type: 'line',
    id: 'active-recovered-chart',
  },

  legend: {
    position: 'top'
  },


  annotations: {
     xaxis: [{
      x: new Date('2020-03-24').getTime(),
      strokeDashArray: 0,
      borderColor: '#808080',
      label: {
        borderColor: '#808080',
        style: {
          color: '#fff',
          background: '#808080',
        },
        text: 'Lockdown starts',
      }
    },
    {
      x: new Date('2020-04-08').getTime(),
      strokeDashArray: 0,
      borderColor: '#00cc66',
      label: {
        borderColor: '#00cc66',
        style: {
          color: '#fff',
          background: '#00cc66',
        },
        text: 'Lockdown impact expected'
      }
    },
    {
      x: new Date('2020-04-29').getTime(),
      strokeDashArray: 0,
      borderColor: '#ff9933',
      label: {
        borderColor: '#ff9933',
        style: {
          color: '#fff',
          background: '#ff9933',
        },
        text: 'Over 70s confinement relaxation'
      }
    },
    {
      x: new Date('2020-05-02').getTime(),
      strokeDashArray: 0,
      borderColor: '#4c9900',
      label: {
        borderColor: '#4c9900',
        style: {
          color: '#fff',
          background: '#4c9900',
        },
        text: 'Phase 1: Retail relaxation'
      }
    },
    {
      x: new Date('2020-05-17').getTime(),
      strokeDashArray: 0,
      borderColor: '#dd6633',
      label: {
        borderColor: '#dd6633',
        style: {
          color: '#fff',
          background: '#dd6633',
        },
        text: 'Phase 1 impact expected',
        position: 'bottom'
      }
    },
    {
      x: new Date('2020-05-18').getTime(),
      strokeDashArray: 0,
      borderColor: '#bfc2c4',
      label: {
        borderColor: '#bfc2c4',
        style: {
          color: '#fff',
          background: '#bfc2c4',
        },
        text: 'Phase 2: Unrestricted movement'
      }
    },
    {
      x: new Date('2020-06-01').getTime(),
      strokeDashArray: 0,
      borderColor: '#42B04D',
      label: {
        borderColor: '#42B04D',
        style: {
          color: '#fff',
          background: '#42B04D',
        },
        text: 'Phase 3: Eat out and transport'
      }
    },
    {
      x: new Date('2020-06-16').getTime(),
      strokeDashArray: 0,
      borderColor: '#5495C7',
      label: {
        borderColor: '#5495C7',
        style: {
          color: '#fff',
          background: '#5495C7',
        },
        text: 'Phase 4: Bars and beaches'
      }
    },
    {
      x: new Date('2020-06-29').getTime(),
      strokeDashArray: 0,
      borderColor: '#00cc00',
      label: {
        borderColor: '#00cc00',
        style: {
          color: '#fff',
          background: '#00cc00',
        },
        text: 'Phase 5: Public gatherings'
      }
    },
    {
      x: new Date('2020-07-15').getTime(),
      strokeDashArray: 0,
      borderColor: '#00dd00',
      label: {
        borderColor: '#00dd00',
        style: {
          color: '#fff',
          background: '#00dd00',
        },
        text: 'Phase 6: Final review'
      }
    },
    {
      x: new Date('2020-12-27').getTime(),
      strokeDashArray: 0,
      borderColor: '#808080',
      label: {
        borderColor: '#808080',
        style: {
          color: '#fff',
          background: '#808080',
        },
        text: 'Curfew starts'
      }
    },
    {
      x: new Date('2021-01-10').getTime(),
      strokeDashArray: 0,
      borderColor: '#00cc66',
      label: {
        borderColor: '#00cc66',
        style: {
          color: '#fff',
          background: '#00cc66',
        },
        text: 'Curfew impact expected'
      }
    },
/*    {
      x: new Date('2020-08-1').getTime(),
      strokeDashArray: 0,
      borderColor: '#00ff00',
      label: {
        borderColor: '#00ff00',
        style: {
          color: '#fff',
          background: '#00ff00',
        },
        text: 'Rock unlock (achievement unlocked)'
      }
    }
*/
    ]
  },
  
  dataLabels: {
    enabled: false
  },
  colors: [
    '#da1f28', '#9bbb59', '#000000', '#66b2ff'
  ],
  stroke: {
    curve: 'straight',
    width: [3, 3, 3, 0]
  },
  grid: {
    padding: {
      right: 30,
      left: 20
    }
  },
  title: {
    text: 'Gibraltar COVID-19 Active vs. Recovered cases',
    align: 'left'
  },

  labels: series.covid19GibraltarSeries.dates,

  xaxis: {
    type: 'datetime',
  },
  yaxis: [{
          title: {
            text: 'Number of people',
          }
        }]
};

// Setup graph showing new cases and 7-day average

var options_newcases = {
  series: [
    {
      data: series.covid19GibraltarSeries.newcases,
      name: &quot;Daily new cases&quot;
    },
    {
      data: series.covid19GibraltarSeries.newcases_avg7d,
      name: &quot;7-day average of new cases&quot;
    }
  ],
  chart: {
    height: 350,
    type: 'line',
    id: 'newcases-tests-chart',
  },

  legend: {
    position: 'top'
  },
 

  dataLabels: {
    enabled: false
  },
  colors: [
    '#000000','#ff00ff'
  ],
  stroke: {
    curve: 'straight',
    width: 2
  },
  grid: {
    padding: {
      right: 30,
      left: 20
    }
  },
  title: {
    text: 'Gibraltar COVID-19 new cases',
    align: 'left'
  },

  labels: series.covid19GibraltarSeries.dates,
  xaxis: {
    type: 'datetime',
  },
  yaxis: [{
          title: {
            text: 'Number of people',
          },
//          decimalsInFloat: 0
        }]

};


// Setup graph showing vaccination progress

var options_vaccination = {
  series: [
    {
      data: series.covid19GibraltarSeries.doses_first,
      name: &quot;First doses administered&quot;,
      type: 'bar'
    },
    {
      data: series.covid19GibraltarSeries.doses_second,
      name: &quot;Second doses administered&quot;,
      type: 'bar'
    },
    {
      data: series.covid19GibraltarSeries.doses_third,
      name: &quot;Third doses administered&quot;,
      type: 'bar'
    },
    {
      data: series.covid19GibraltarSeries.doses_booster,
      name: &quot;1st booster administered&quot;,
      type: 'bar'
    },
    {
      data: series.covid19GibraltarSeries.totaldoses,
      name: &quot;Doses shipped (6-doses per vial)&quot;,
      type: 'line' // was line
    }
  ],
  chart: {
    height: 350,
    type: 'line',
    id: 'vaccine-tests-chart',
    stacked: true,
    animations: {
      enabled: false
    }
  },

  plotOptions: {
    bar: {
      horizontal: false,
      columnWidth: '90%',
      // endingShape: 'rounded'
    },
  },

  legend: {
    position: 'top'
  },
 

  dataLabels: {
    enabled: false
  },
  colors: [
    '#4c9900',  '#36D43B', '#7AF37E', '#66ff66', '#000000',
  ],
  stroke: {
    curve: ['smooth', 'smooth', 'smooth', 'smooth', 'stepline'],
    width: 2
  },
  fill: {
    opacity: 100,
    type: ['solid', 'solid', 'solid', 'solid', 'solid'],
  },
  grid: {
    padding: {
      right: 30,
      left: 20
    }
  },
  title: {
    text: 'Gibraltar COVID-19 vaccination progress',
    align: 'left'
  },

  labels: series.covid19GibraltarSeries.dates,
  xaxis: {
    type: 'datetime',
    min: new Date(&quot;01 January 2021 GMT&quot;).getTime(),
  },
  yaxis: [{
          title: {
            text: 'Number of vaccines administered',
          },
//          decimalsInFloat: 0
        }]

};


// Setup graphs showing the testing results

var options_tests = {
  series: [
    {
      data: series.covid19GibraltarSeries.tests_received,
      name: &quot;Received&quot;
    },
    {
      data: series.covid19GibraltarSeries.tests_pending,
      name: &quot;Pending&quot;
    }
  ],
  chart: {
    height: 350,
    type: 'line',
    id: 'active-tests-chart',
    stacked: true
  },

  legend: {
    position: 'top'
  },
  

  annotations: {
     xaxis: [{
      x: new Date('2020-04-01').getTime(),
      x2: new Date('2020-04-07').getTime(),
      strokeDashArray: 0,
      borderColor: '#775DD0',
      label: {
        borderColor: '#775DD0',
        style: {
          color: '#fff',
          background: '#775DD0'
        },
        text: 'Random testing',
      }
    },
    {
      x: new Date('2020-04-10').getTime(),
      strokeDashArray: 0,
      borderColor: '#0854a5',
      opacity: 0.3,
      label: {
        borderColor: '#0854a5',
        style: {
          color: '#fff',
          background: '#0854a5'
        },
        text: 'Local testing starts',
        position: 'bottom'
      }
    },
    {
      x: new Date('2020-04-27').getTime(),
      x2: new Date('2020-05-12').getTime(),
      strokeDashArray: 0,
      borderColor: '#b266ff',
      label: {
        borderColor: '#b266ff',
        style: {
          color: '#fff',
          background: '#b266ff'
        },
        text: 'Front-line random testing',
        position: 'bottom'
      }
    }]
  },
  dataLabels: {
    enabled: false
  },
  colors: [
    '#39639d','#323232'
  ],
  stroke: {
    curve: 'straight',
    width: 3
  },
  grid: {
    padding: {
      right: 30,
      left: 20
    }
  },
  title: {
    text: 'Gibraltar COVID-19 Tests received and pending',
    align: 'left'
  },

  labels: series.covid19GibraltarSeries.dates,
  xaxis: {
    type: 'datetime',
  },
  yaxis: [{
          title: {
            text: 'Number of people',
          }
        }]

};

// Configure and show charts for total cases

var options_total = {
  series: [{
      data: series.covid19GibraltarSeries.totalcases,
      name: &quot;Infected&quot;
    },
/*    {
      data: series.covid19GibraltarSeries.recovered,
      name: &quot;Recovered&quot;
    }
  */  
  ],
  chart: {
    height: 350,
    type: 'line',
    id: 'total-chart',
  },

  legend: {
    position: 'top'
  },

  annotations: {
     xaxis: [{
      x: new Date('2020-03-24').getTime(),
      strokeDashArray: 0,
      borderColor: '#808080',
      label: {
        borderColor: '#808080',
        style: {
          color: '#fff',
          background: '#808080',
        },
        text: 'First lockdown starts',
      }
    },
    {
      x: new Date('2021-01-02').getTime(),
      strokeDashArray: 0,
      borderColor: '#808080',
      label: {
        borderColor: '#808080',
        style: {
          color: '#fff',
          background: '#808080',
        },
        text: 'Second lockdown starts',
        position: 'bottom'
      }
    }]
  },

  dataLabels: {
    enabled: false
  },
  colors: [
    '#000000',
    '#9bbb59'  
  ],
  stroke: {
    curve: 'straight',
    width: 3
  },
  grid: {
    padding: {
      right: 30,
      left: 20
    }
  },
  title: {
    text: 'Gibraltar COVID-19 total cases',
    align: 'left'
  },

  labels: series.covid19GibraltarSeries.dates,

  xaxis: {
    type: 'datetime',
  },
  yaxis: [{
          title: {
            text: 'Total cases',
          },
          seriesName: 'Total cases',
          // logarithmic: true
  }]
  
};

var chart_active_recovered = new ApexCharts(document.querySelector(&quot;#chart_active_recovered&quot;), options_active_recovered);
var chart_newcases = new ApexCharts(document.querySelector(&quot;#chart_newcases&quot;), options_newcases);
var chart_vaccination = new ApexCharts(document.querySelector(&quot;#chart_vaccination&quot;), options_vaccination);
var chart_tests = new ApexCharts(document.querySelector(&quot;#chart_tests&quot;), options_tests);
var chart_total = new ApexCharts(document.querySelector(&quot;#chart_total&quot;), options_total);

chart_active_recovered.render();
chart_newcases.render();
chart_vaccination.render();
chart_tests.render();
chart_total.render();

// Setup scale handler

function setLogarithmicScale(logarithmic) {
  options_total.yaxis[0].logarithmic = logarithmic;
  ApexCharts.exec('total-chart', 'updateOptions', { options_total }, false, true);
}

document.scale.logarithmic.checked = true;
setLogarithmicScale(true);
document.scale.linear.addEventListener('click', function() { setLogarithmicScale(false); document.scale.logarithmic.checked = false; document.scale.linear.checked = true; });
document.scale.logarithmic.addEventListener('click', function() { setLogarithmicScale(true); document.scale.linear.checked = false; document.scale.logarithmic.checked = true; });


&lt;/script&gt;</content><author><name></name></author><category term="gibraltar" /><summary type="html">The Gibraltar Government publishes a daily COVID-19 report that includes number of conducted tests, how many cases are active and how many people have recovered. There is no historical information available so I have recorded the series since these started being published on the 12th March 2020 and generated the charts below. The raw numbers can be downloaded so you can run your own analysis.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/covid.png" /><media:content medium="image" url="http://flow.gi/covid.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">BSides San Francisco 2020 write-up</title><link href="http://flow.gi/BsidesSF2020/" rel="alternate" type="text/html" title="BSides San Francisco 2020 write-up" /><published>2020-02-24T00:00:00+00:00</published><updated>2020-02-24T00:00:00+00:00</updated><id>http://flow.gi/BsidesSF2020</id><content type="html" xml:base="http://flow.gi/BsidesSF2020/">&lt;p&gt;Key highlights of the sessions and hacking villages that I attended at the 2020 BSidesSF conference in San Francisco. This year the conversation inspired discussions around changing the way in which we work in cybersecurity, by getting people more involved or approaching the same problems in a different, more efficient way. Read on to find out what the SF cybersecurity scene had to say.&lt;/p&gt;

&lt;p&gt;I thoroughly enjoyed my first BSidesSF conference, a volunteer-run engineering-oriented security conference which deals with real problems that security teams face day in and day out. As the name implies the BSides conference is always paired with a more mainstream security conference (in this case it happens on the two days before the &lt;a href=&quot;https://www.rsaconference.com/&quot;&gt;RSA Conference&lt;/a&gt;). I had high expectations after attending BSides London a few years back in the run-up to 44Con, and the San Francisco version did not let me down.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/bsides_sf_2020.png&quot;&gt; &lt;img src=&quot;/images/bsides_sf_2020.png&quot; alt=&quot;&quot; title=&quot;BSides SF 2020&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;sessions&quot;&gt;Sessions&lt;/h1&gt;

&lt;p&gt;Sessions were one of the main reasons why I attended the conference as it provides a wealth of knowledge of what other organisations security teams are facing without the vendor pitch. Below is a rendition in chronological order of the chats I attended - there were up to four tracks running in parallel so this is by no means a representation of everything that went on at BSides. You can find recordings of these sessions and all the other ones &lt;a href=&quot;https://www.youtube.com/channel/UCWemrSP6Aba171jXReCz_Qg/playlists&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;keynote-for-day-one-give-away-securitys-legos-dumping-traditional-security-teams-by-fredrickl&quot;&gt;Keynote for Day One: Give Away Security’s Legos: Dumping Traditional Security Teams by @fredrickl&lt;/h2&gt;

&lt;p&gt;The keynote for the first day was a fresh approach to the “you are doing it wrong” theme: move away from the bureaucracy that your security organisation may be trapped in by devolving security to the departments you usually work with, let them take ownership, and allow the team to focus on doing real cybersecurity work that no-one else at the company can do: threat hunting, developing security components and consulting on security topics.&lt;/p&gt;

&lt;p&gt;The presentation was full of pragmatic approaches on how to do this, here are some of my personal takeaways (but I strongly recommend you watch the recording of the presentation, Flee has a lot to say):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;You do not have the full context of your business&lt;/em&gt;: during security reviews we spend a lot more time trying to familiarise ourselves with the proposed change and the business it supports than actually conducting the security review. While I agree that we cannot be in the middle of every single change, there still needs to be exposure of the cybersecurity team to the business, we need to be able to understand our organisation challenges and do security that makes sense and is aligned with the company strategy.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Product manager involvement&lt;/em&gt;: moving the responsibility for threat assessments and security reviews to Product Management, making security part of their product (like we always preach) but letting them do the risk assessments and discuss with the Product Owners the prioritisation (something I never do). But see my point above, we still need to be close to Product, just not in the middle of it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Awareness training&lt;/em&gt;: moving away from the generic “policy” mandatory training and doing role-focused training, giving each team the knowledge and tools they know to &lt;em&gt;do security&lt;/em&gt; - not just to follow the rules. Train them on how to do threat analysis, risk assessments, manage vulnerabilities and how to use the tools that the organisation has invested in.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Table top exercises&lt;/em&gt;: we have done paper exercises to simulate attacks and crisis management, however modelling after a D&amp;amp;D game may be the way to go to get people really engaged and after all, as Flee says, everyone loves D&amp;amp;D - even if they do not yet know it :-)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Security people are not the smartest. Security people get it wrong&lt;/em&gt;: we seem to be at the centre of the universe, hiding behind the disguise of consultants and advisors when we really are the gatekeepers/cops. People humour us as a necessary evil, but really are happy to see our back and get on with their work. We are neither the smarter kids on the block and can also do mistakes, so stop pretending - letting the other teams do security makes the organisation take better security decisions.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having the risk ownership with to the business owners, where it really resides, allows the right people to make the right decisions when it comes to trade-offs - supported whenever necessary by the security team. We should spend less time trying to get our fingers in all the pies, pretending we are the centre of the business and focusing on delivering the outcomes we are uniquely placed to deliver.&lt;/p&gt;

&lt;h2 id=&quot;graph-based-detection-and-response-with-grapl-by-insanitybit&quot;&gt;Graph Based Detection and Response with Grapl by @insanitybit&lt;/h2&gt;

&lt;p&gt;Although some of the security products we are currently using are already incorporating graph-based analysis (endpoint protection, network anomaly detection or auditing - like the speaker-referenced &lt;a href=&quot;https://github.com/BloodHoundAD/BloodHound&quot;&gt;BloodHoundAD&lt;/a&gt; come to mind), I thought that &lt;a href=&quot;https://github.com/insanitybit/grapl&quot;&gt;Grapl&lt;/a&gt;, the open-source tool that the speaker has developed, was a novel way of looking at attack detection beyond traditional log analysis.&lt;/p&gt;

&lt;p&gt;The concept is simple and powerful, which typically a good combination: you feed logs to the tool in real-time and it applies a set of graph-based models to detect when something is not as it should be, not that different to how ESP works.&lt;/p&gt;

&lt;p&gt;The models are defined in python, which means you do not need to learn an additional language and that you can apply all software engineering best practices to it (repositories, versioning, sharing, testing…).&lt;/p&gt;

&lt;p&gt;GRAPL parses logs using a set of connectors (sysmon and a json-specific format) and converts this into a graph database, adding nodes and edges as each log provides additional information to an existing process, network connection, user, etc. This means that your detection rules no longer need to operate on a single log-line (or even the context of the past 30minutes of log-lines) but on an entity that evolves and that is enriched by every log line that is received. This makes it really simple something that is difficult for SIEMs without using unapproachable amount of memory: matching logs from 2/3 different sources over a period beyond a few hours.&lt;/p&gt;

&lt;p&gt;This graph database is then analysed using a set of rules, such as “identify all nodes started by Word/Adobe/Excel/Outlook that have created subprocesses that have connected to an external IP address and downloaded a new file that was then subsequently ran” or “identify all services that are exposed externally and that have executed a subprocess less than 5 times in the past month”, etc. I can see the value of sharing these rule-sets which are technology-agnostic and really try to model process and user behaviour, something similar to what Microsoft is doing with Sentinel play-books.&lt;/p&gt;

&lt;p&gt;The concept of “lenses” was also introduced as a way of exploring and doing forensics by starting at a node and moving through it to other nodes.&lt;/p&gt;

&lt;p&gt;I can’t really wait to give Grapl a try!&lt;/p&gt;

&lt;p&gt;The speaker hinted at interest from commercial vendors for the solution, so I guess the advice is to fork it while it lasts :-)&lt;/p&gt;

&lt;h2 id=&quot;secure-by-design-usable-security-tooling-by-hxnyk&quot;&gt;Secure by Design: Usable Security Tooling by @hxnyk&lt;/h2&gt;

&lt;p&gt;An interesting, although brief, discussion on how improving the usability of security tools leads to increased adoption amongst your internal customers.&lt;/p&gt;

&lt;p&gt;As a security engineer I tend to focus a lot on the core functionality of the application I am working on, e.g. what makes it different from anything else out there. I spend a lot of time on that &lt;em&gt;core&lt;/em&gt; functionality, testing, being amazed at how awesome the research results are and celebrating. It is only at that point that I just patch together a UI around it, as quickly as I can, before I release it to the world.&lt;/p&gt;

&lt;p&gt;It is usually at that stage when the application fails.&lt;/p&gt;

&lt;p&gt;Hon’s talk starts with a typical security engineer designed UI, pointing at several common and easy to overcome flaws such as confusing controls (my favourite sin), lack of intuitiveness, no deep-linking ability, etc. All of these flows make the application intimidating and, to quote Han “Instead of learning security the user need to learn about your tool before they can start learning security”.&lt;/p&gt;

&lt;p&gt;While these are easy to avoid flaws, the secret is on putting yourselves in the user shoes and using the work that already exists out there: if your company does public-facing products involve your UX designers in the development of your tool, sit down with users and listen to them and apply usability best practices such as &lt;a href=&quot;https://www.nngroup.com/articles/ten-usability-heuristics/&quot;&gt;Nielsen’s heuristics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While I agree with most of the arguments in favour of the importance of a correctly designed UI to improve user engagement, there is one concept that I cannot get myself to agree on “UI before API”, e.g. designing the API around the UI instead of the other way around. To me an API needs to be created around the business domain and the models your application handles more than with a specific UI in mind. APIs exist only for a purpose: separating the frontend from the business logic concerns, so that any number of UIs can be created without having to modify the back-end. I cannot really see any API that I design be driven for how the UI will represent the information!&lt;/p&gt;

&lt;h2 id=&quot;the-red-square-mapping-the-connections-inside-russias-apt-ecosystem-by-arieitan&quot;&gt;The Red Square: Mapping the Connections Inside Russia’s APT Ecosystem by @arieitan&lt;/h2&gt;

&lt;p&gt;A brief presentation by Ari Eitan at Intezer regarding an analysis of several APTs attributed to Russia state-sponsored groups, breaking down its components and trying to establish patterns amongst different apparently independent criminal groups. The findings proved that there was no technical link between the various APT groups, however whether this was by design or as a form of misdirection was left as an exercise for the reader. Of the various presentations this is the one that felt the most commercial, with the work based on proprietary technology only available to Intezer, so difficult to validate. They presented a couple of tools that were the result of the study, both available at apt-ecosystem.com.&lt;/p&gt;

&lt;h2 id=&quot;peeling-the-web-application-security-onion-without-tears-by-noam-lorberbaum-and-keith-mashinter&quot;&gt;Peeling the Web Application Security Onion Without Tears by Noam Lorberbaum and Keith Mashinter&lt;/h2&gt;

&lt;p&gt;A detailed view into how Adobe has re-architected Adobe Document Cloud and how they have leveraged existing security technologies to provide a defence in-depth approach, without reinventing the wheel. It was good to see how similar challenges are resolved in similar ways by different organisations, from providing DDoS protection layers to addressing the security of API gateways.&lt;/p&gt;

&lt;p&gt;In an effort that would not be alien to any organisation that has to comply with multiple regulations across different jurisdictions, Adobe has created a &lt;a href=&quot;http://www.adobe.com/pdf/Open_Source_CCF.pdf&quot;&gt;common control framework&lt;/a&gt; and open-sourced it so that other organisations can put it in place - good reading to any organisation that operates any customer-facing product.&lt;/p&gt;

&lt;h2 id=&quot;keynote-for-day-two-whats-new-or-not-in-2020-are-we-making-progress-on-the-intractable-security-problems-by-larkinryder&quot;&gt;Keynote for Day Two: What’s New or Not in 2020: Are we Making Progress on the Intractable Security Problems? by @larkinryder&lt;/h2&gt;

&lt;p&gt;A review of what major shifts in cybersecurity over the past decade, presented in an entertaining way so make sure you check the whole video. Some of my key takeaways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Third-parry trust&lt;/em&gt;: we are doing it too manually and too “point in time”, we need a better way to manage the trust of third-parties as we increasingly rely on them in our organisations&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Privacy&lt;/em&gt;: an enabler to do security right over the past few years, take GDPR or the CCPA as a way to ensure that organisations are doing the right thing. A point that, without disagreeing, leaves many things unsaid on the values of those organisations that are only driven by compliance when it comes to the security of their customers and stakeholders.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Cloud cloud cloud&lt;/em&gt;: a common theme across the conference, everyone is shifting loads to the cloud and it is becoming rarer to find organisations that manage their own infrastructure. There were a couple of very visual slides on how the skill-set of Technology teams have moved a couple of layers up and is not focused on Applications. Through the conference and no matter who you spoke with it was difficult to find anyone that is running on-premises systems any more.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Mobile ubiquity is good for security&lt;/em&gt;: the push to be able to connect to all our data from wherever we are and through our mobile devices has opened opportunities to increase security authentication using mobile built-in 2FA mechanisms or even biometrics. We should leverage the use of devices to better identify our customers. On the other side losing mobile devices has become a real problem, encryption is a must if you do not want to see your data stolen.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Customer data is off limits&lt;/em&gt;: long gone are the days where customer data was passed around between teams with disregard on user information security. Nowadays it is difficult to demonstrate why a team needs access to personal data, creating guard-rails to ensure that data access is appropriately protected and audited goes a long way to avoid breaches on that information. One very interesting though that was presented is trying to understand how much criminals resell breached information for so that we understand what is an appropriate investment (e.g. if bad guys are selling passport information for USD1,000 then it makes sense we invest a similar amount of money to protect them).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;protecting-the-bridge-from-dollars-to-bitcoin-securing-coinbasess-edge-payments-infrastructure-by-nishil-shah&quot;&gt;Protecting the Bridge from Dollars to Bitcoin: Securing Coinbases’s Edge Payments Infrastructure by Nishil Shah&lt;/h2&gt;

&lt;p&gt;A deep-dive on how payment methods work in the US (and abroad) and how difficult it is to protect loses with the controls built into payment systems that have been around for decades, well before trust between the different entities that participate in a transaction was an issue. A threat analysis of various payment method flows was presented with call-out to understand what could go wrong and how companies address it.&lt;/p&gt;

&lt;p&gt;Coinbase integrates lots of different payment methods, some of which are more creative than others - to ensure security on those payment methods a three-step process is followed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Review the new payment provider during the procurement phase, and stop it at that stage if it does not provide appropriate assurances&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Regularly test the payment integrations&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Incorporate security test cases in the integrations developed, by having security champions embedded in the payments engineering teams&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the interesting thoughts in Nishil’s presentation was whether a payment bug that led to money being lost was really an application security topic (something we can also extend to other items that contribute to fraud). The bottom line is that if we define security as ensuring that the amount of fraud is prevented then it falls spot on in our area, so do not shy away and work with those teams - even if it is not strictly your responsibility: in the end we are all trying to make our organisation as successful as possible, and preventing fraud sounds like a great way to contribute to that goal.&lt;/p&gt;

&lt;h2 id=&quot;lessons-learned-from-the-devsecops-trenches&quot;&gt;Lessons Learned from the DevSecOps Trenches&lt;/h2&gt;

&lt;p&gt;A panel of application security leads, CISOs, Red Teams leads and security researchers that have been working in DevSecOps for a while and that shared some of their success stories and pitfalls to avoid:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@astha_singhal, Director of Application Security, Netflix
@dugdep, Director of Defense, Datadog
@justine_osborne, Offensive Security Technical Lead, Apple
@zanelackey, Chief Security Officer, Signal Sciences
@clintgiblertldrsec.com, Research Director, NCC Group
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The panel addressed topics in various areas which I have tried to capture below, however it is probably better if you watch the panel itself and form your own conclusions.&lt;/p&gt;

&lt;h3 id=&quot;failure-stories&quot;&gt;Failure stories&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Testing matters - think about the actual operation of the tool you are developing before you release on your production environment&lt;/li&gt;
  &lt;li&gt;Set clear success criteria before you spend a year on projects that are going nowhere&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;asset-inventory-if-you-were-to-start-embarking-on-a-path-from-scratch-how-would-you-approach-it&quot;&gt;Asset inventory: if you were to start embarking on a path from scratch how would you approach it?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Gotcha: realise that you do not need just security engineers, but data engineering analysts so that the tool scales&lt;/li&gt;
  &lt;li&gt;Positive: organisational side, send security champions to meetings with the product and build infrastructure team to learn and listen on new products that are coming up&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;build-vs-buy-decisions&quot;&gt;Build vs Buy decisions&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The key decision point are the resources that need to be dedicated to development and maintenance&lt;/li&gt;
  &lt;li&gt;Based on the vendor you are working with, getting 50% immediately from a vendor may be better than 100% in a few years time!&lt;/li&gt;
  &lt;li&gt;Get 80% coverage as quick as possible, so that when the resources are available they can go into the remaining 20%&lt;/li&gt;
  &lt;li&gt;Be open to deprecating internally developed tools once the vendors catch-up&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;security-analysis-in-pipelines-in-cicd&quot;&gt;Security analysis in pipelines in CI/CD&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“Trying to find all the bugs” vs “Are we using the frameworks in use”? It is better to build guardrails for developers than trying to find complex issues (e.g. use mutual-TLS between services, use secret management, etc.)&lt;/li&gt;
  &lt;li&gt;A lot of critical vulnerabilities found at Netflix could have been resolved by implementing best practices, so they focused on the controls, e.g. not avoiding the vulnerabilities but limiting the impact.&lt;/li&gt;
  &lt;li&gt;..but it’s not always about using security components to get security but default but &lt;em&gt;doing&lt;/em&gt; security and understanding unique risks as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;red-teams-how-do-you-make-sure-it-is-improving-the-security-posture&quot;&gt;Red Teams: how do you make sure it is improving the security posture&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Measuring security risk is a hard problem, but security testing boils down to hypothesis, experiment and analyse the results. Defining the correct hypothesis for the organisation is the key.&lt;/li&gt;
  &lt;li&gt;Red Teams allow to see the bigger picture: there are always loopholes, etc. It also allows to move beyond jut AppSec and find issues in other areas (infra, corp)&lt;/li&gt;
  &lt;li&gt;The impact is not just the number of bugs found, but the new types of attacks you are identifying as a blue team&lt;/li&gt;
  &lt;li&gt;Do not call it done until all the things you found are no longer possible to be exploited&lt;/li&gt;
  &lt;li&gt;Red Teams can also be a good tool, but you also need to balance that by not breaking trust with internal customers (e.g. so you are not perceived as showing how dumb they are by breaking into their systems)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;threat-modelling-and-security-reviews-how-important-and-how-do-you-scale&quot;&gt;Threat modelling and security reviews: how important and how do you scale?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Use MITRE ATT&amp;amp;CK framework as the basis for thread modelling, so that the whole detection is improved&lt;/li&gt;
  &lt;li&gt;Scaling thread model is a requirement: self-service questionnaires for developers (so that the output recommends specific guidance on what to do and it computes a risk score, so the dev security can focus on the riskiest), or integrating threat modelling on the development life-cycle or thread-model as code (e.g. adding security tests as abuse in integration testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;appsec-pipelines-what-staticdynamic-analysis-has-brought-the-highest-roi&quot;&gt;AppSec pipelines: What static/dynamic analysis has brought the highest ROI?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Really targeted scanning, e.g. instead of doing everything well focus on a class of bugs and kill it everywhere&lt;/li&gt;
  &lt;li&gt;Start bottoms-up and define initial security controls, so that the development team self-manages the issues and their resolution.&lt;/li&gt;
  &lt;li&gt;Focus on dangerous patterns that are specific to the frameworks in use&lt;/li&gt;
  &lt;li&gt;Identify as quickly as possible, e.g. on code commit or in the IDE - or at the worst case in CI/CD pipeline&lt;/li&gt;
  &lt;li&gt;Finding vulnerable dependencies in close partnership with build teams (they have the same challenges to manage dependencies) so that we can flag dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-is-the-most-effective-engagement-and-awareness-initiative-you-have-done-with-your-engineering-teams&quot;&gt;What is the most effective engagement and awareness initiative you have done with your engineering teams?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Find ways to have engineering teams engaged with security, so that security is centrally located or balls of candy on people coming to central team. Budget for drinks on engineering teams happy hour last two rounds :-)&lt;/li&gt;
  &lt;li&gt;Asking Engineering to join the Red Team the exploitation of a vulnerability&lt;/li&gt;
  &lt;li&gt;Create a good experience when folks come to talk to you, e.g. be realistic on the goals and be as responsible as possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;new-organisation-what-would-be-the-first-investment-you-will-do&quot;&gt;New organisation: what would be the first investment you will do?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;2 x Visibility into what you have, what it is and who owns it - e.g. visibility on your systems&lt;/li&gt;
  &lt;li&gt;Protect a number of security analysts from interruptions so they can take the long-view on some hard problems (e.g. framework creation, etc.)&lt;/li&gt;
  &lt;li&gt;Make sure you understand the risk you are trying to reduce at the enterprise level (e.g. what are the corporate nightmares when it comes to risk) so you can prioritise&lt;/li&gt;
  &lt;li&gt;Going into a new security roles: get the buy-in from the other teams, they for sure had negative experiences in the past, so you need to change that perception for future successes&lt;/li&gt;
  &lt;li&gt;Vulnerability management: so you can measure when something worked or not&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2fa-in-2020-and-beyond-by-kelleyrobinson&quot;&gt;2FA in 2020 and beyond by @kelleyrobinson&lt;/h2&gt;

&lt;p&gt;This was a really useful session with Kelley presenting research on the usability and effectiveness of various two-factor authentication mechanisms, covering SMS, TOTP, Push and Physical security keys. I believe that a quote from the speaker “We are so owned passwords are no longer safe” mimics reality, for all our important accounts we are demanding further authentication mechanisms, so I found the talk very interesting - specially if you are deciding what next 2FA mechanism to deploy. I recommend you watch the whole presentation as it covers a lot of useful statistics around 2FA methods penetration and usability.&lt;/p&gt;

&lt;p&gt;Some of the results of the research were learnings for me, specifically the following two:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Push is not as effective as we think&lt;/em&gt;: It is probably the easiest and less-friction mechanism for 2FA, but this seamlessness makes users feel that the system is not adding that much security. It does not help that there is no standard around Push security approvals and that everyone needs to install its own app for this.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;TOTP has a wider adoption than thought&lt;/em&gt;: It combines the security that SMS cannot offer, the ability to work offline and it only requires the user to install an app - which they have already done if they are using YOUR app. Entering the codes gives the right amount of security perception that customers are looking for, and it uses open standards so there are multiple ways of getting the codes.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;otr-tears-from-the-cloud-by-theckman&quot;&gt;OTR: Tears from The Cloud by @theckman&lt;/h2&gt;

&lt;p&gt;BSidesSF has introduced a new type of sessions this year labelled OTR (“Off the Record”) where any recording of the session or discussion on what was mentioned is discouraged, in a concept similar to the DEFCON &lt;a href=&quot;https://skytalks.info/&quot;&gt;Sky Talks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These sesssions are out of bounds for the press, with the goal of having open and frank discussions without the risk of being shot down for bringing up sensitive topics. I attended this particular session and, while I cannot really discuss what was mentioned, I think that the OTR sessions are still far from perfect both in content and interest - and from what I heard from people in my team that attended DEFCON this year the same can be said for the Sky Talks.&lt;/p&gt;

&lt;h2 id=&quot;mental-health-for-hackers-contents-under-pressure-by-v33na-ryanlouie-and-chloemessdaghi&quot;&gt;Mental Health for Hackers: Contents under pressure by @v33na, @ryanlouie and @ChloeMessdaghi&lt;/h2&gt;

&lt;p&gt;This was the final session I attended at BSidesSF this year, I wanted to understand the specific mental problems that people in Cybersecurity face so I can be proactive in identifying, thinking of team sanity more than anything else. While the session was useful on getting some background information it lacked specifics on how employment in a cyber position contributes to these mental health issues. We know people in our space are affected by Anxiety, Depression or Burnout - but what is it in our job that makes these problems so prominent and what can we do about it?&lt;/p&gt;

&lt;p&gt;One of the key points I took away was the concept that all of these problems are easier to handle if you share it with someone, if you do not go through it alone - it’s the community that matters and both in cyber and security research there is a large amount of those. So do not do it alone :-)&lt;/p&gt;

&lt;p&gt;The closing notes of the presentation gave some pointers for further research:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://psychyatry.org/patients/families&quot;&gt;American Psychiatric Association&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.nimh.nih.gov/healts/find-help/index.shtml&quot;&gt;National Institute of National Health&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://who.int/mental_health/en/&quot;&gt;World Health Organization&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://mentalhealthhackers.org/&quot;&gt;Mental Health Hackers @HackersHealth&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;villages&quot;&gt;Villages&lt;/h1&gt;

&lt;p&gt;There are five “hacking” villages where you can learn the basics around tinkering with various areas that require some basic hardware you may not have around your workshop, using a self-learning tutorial style - just sit down and get started:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;IoT: a very introductory tutorial around firmware analysis, spotting hard-coded credentials and exploiting them through telnet. Learnt about binwalk as a tool to identify known structures in a packed firmware (for instance the bootloader, a squash FS, etc.) and extracting them, really cool and something that goes beyond my “strings” skills.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reverse Engineering: a focus on reverse-engineering firmware from a Buffalo NAS server, identifying and exploiting vulnerabilities. I learnt a couple of tricks such as the uncompyle6 tool for decompiling .pyc to an approximation of the original Python source code. Throughout the tutorial&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Car hacking: Simulating a set of vehicle components, the lab focused around CAN bus hacking, which is an area we had touched in the past when doing SCADA security research with @xpanadero for S4. Unfortunately the environment was being reset when I sat down so did not get to learn anything new.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conference was packed with sessions so I was unable to sit down at the Lockpicking or the Crypto hacking villages.&lt;/p&gt;

&lt;h1 id=&quot;vendors&quot;&gt;Vendors&lt;/h1&gt;

&lt;p&gt;I did not spend that much time visiting vendors booths as I will have plenty of opportunities at RSA during the rest of the week, but there were three that caught my eye:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://cmd.com/&quot;&gt;&amp;gt;_cmd&lt;/a&gt;: a command-line auditing and blocking solution, similar to what Balabit are doing with &lt;a href=&quot;https://www.oneidentity.com/products/one-identity-safeguard-for-privileged-sessions/&quot;&gt;PSM&lt;/a&gt;, but without the RDP or file transfer support. Instead of installing a gateway before accessing the system. _cmd is installing as an agent on each Linux system (it only supports Linux) which injects a library into each process which (I assume) patches syscalls so that it can monitor what the application is doing. It sends the recorded events (input/output/UID change/etc.) to a cloud-based central repository that can be used for auditing. Activities can also be blocked from happening. It feels like a good solution, but given the limited platform availability I am not sure if we are not better off with auditd for the time being. The cloud-based console is cool though, and it makes it easy to review sessions but, again, only for Linux based systems.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://snyk.io&quot;&gt;snykj&lt;/a&gt;: an open source vulnerability tracking outfit that helps identify known vulnerabilities that you are importing into your code through dependencies, similar to what &lt;a href=&quot;https://whitesourcesoftware.com&quot;&gt;WhiteSource&lt;/a&gt; or &lt;a href=&quot;https://blackducksoftware.com&quot;&gt;BlackDuck&lt;/a&gt; are doing.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://nightfall.ai/&quot;&gt;nightfall AI&lt;/a&gt;: a cloud-based solution that monitors your information and automatically classifies based on its content and certain AI models, allowing you to apply DLP-like policies to it. It has connectors for several cloud-based services, however does not support on-premises systems, which limits its use in environments where some of the critical assets are still maintained internally, think legacy databases.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;/h1&gt;

&lt;p&gt;All in all a very enjoyable and useful conference, created by and for the community and for a meagre USD50 that covers sessions, villages, free swag, stickers, food, drinks and a lot of fun. There seems to be a shift in focus from extremely technical sessions towards a more “how do we improve security by looking at the human side” in general, something that I also feeling as RSAC2020 gets started (this years’s motto is “Human Element”). Let’s see where this leads from here, if you have any feedback/correction or further thoughts please reach out on twitter (@lluismh).&lt;/p&gt;</content><author><name></name></author><category term="security" /><summary type="html">Key highlights of the sessions and hacking villages that I attended at the 2020 BSidesSF conference in San Francisco. This year the conversation inspired discussions around changing the way in which we work in cybersecurity, by getting people more involved or approaching the same problems in a different, more efficient way. Read on to find out what the SF cybersecurity scene had to say.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/bsides_sf_2020.png" /><media:content medium="image" url="http://flow.gi/bsides_sf_2020.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Azure AD authentication and authorisation in Angular applications</title><link href="http://flow.gi/AngularAzureADAuthenticationAndAuthorization/" rel="alternate" type="text/html" title="Azure AD authentication and authorisation in Angular applications" /><published>2020-01-06T00:00:00+00:00</published><updated>2020-01-06T00:00:00+00:00</updated><id>http://flow.gi/AngularAzureADAuthenticationAndAuthorization</id><content type="html" xml:base="http://flow.gi/AngularAzureADAuthenticationAndAuthorization/">&lt;p&gt;There is plenty of documentation on integrating javascript applications with Microsoft cloud authentication, however there is little information on how to define which users are allowed to log-in, managing them and assigning the roles your application uses to them using Azure AD. Read on to learn how to do an end-to-end integration of Angular, Azure AD and user+role management.&lt;/p&gt;

&lt;p&gt;I recently had the need to add cloud-based Microsoft authentication to an angular+rails application which uses role-based access control, including integrating authorization of users who are assigned roles directly in AzureAD. This removes the need for the application from having to manage users locally (urgh) and separates the management of the application from the management of its users (yey!).&lt;/p&gt;

&lt;p&gt;Users choose to log-in using their Microsoft AzureAD credentials (aka their Office365 account) by clicking on a link on the application login page. They are then redirected to the Microsoft login page and, once authenticated, a JWT token is sent back to the application which includes the users roles. The application extracts these roles and uses them to authorise the user access.&lt;/p&gt;

&lt;p&gt;To achieve this we will implement the following steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#register-your-application-with-azure&quot;&gt;Register your application with Azure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#assign-application-roles-to-users-or-groups&quot;&gt;Assign application roles to users or groups&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#integrate-azuread-authentication-with-your-application&quot;&gt;Integrate AzureAD authentication with your application&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#access-azuread-jwt-token-and-extract-roles&quot;&gt;Access AzureAD JWT token and extract roles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While there are plenty of articles and libraries that integrate angular and azure authentication, I did not find much information on using azure for user application role management, I hope you find this article and the &lt;a href=&quot;https://github.com/llmora/azuread-angular-example&quot;&gt;accompanying code&lt;/a&gt; useful.&lt;/p&gt;

&lt;h2 id=&quot;register-your-application-with-azure&quot;&gt;Register your application with Azure&lt;/h2&gt;

&lt;p&gt;Azure needs to learn about your application in advance (amongst other things to decide which user repository to authenticate against and where to send authentication results to).&lt;/p&gt;

&lt;p&gt;To register the application you need to access the &lt;a href=&quot;https://aad.portal.azure.com/&quot;&gt;Azure Active Directory admin centre&lt;/a&gt;, either get permissions or find a friendly Azure admin in your organisation who can help you with making the required changes. The Microsoft admin centres are a bit like shifting sand, so the names of the options below may have changed since this article was published - if you can’t find the specific option mentioned try to find for something that sounds similar…&lt;/p&gt;

&lt;p&gt;After you login to the Azure Active Directory admin centre click on “Azure Active Directory” on the left menu, then select “App Registrations” from the Manage section and register the application you want to integrate with Azure AD.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/azuread_appreg.png&quot;&gt; &lt;img src=&quot;/images/azuread_appreg.png&quot; alt=&quot;&quot; title=&quot;Application registration in Azure AD&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The information required during registration is quite straight-forward, but here is a brief description to help you fill in the details:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: a descriptive name that will be used to refer to your application, we will use “testapp” throughout the article&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Supported account types&lt;/strong&gt;: select the scope of user accounts that you want authenticating against your application, either restricted to your own Office365 tenant, to other Office365 tenants or including personal accounts. For our application we will go with “Single tenant”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Redirect URI&lt;/strong&gt;: enter the URL that your users will be clicking on the ‘Login with Microsoft’ button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After submitting the registration form, we will be provided with two of the key values we need to configure Azure authentication in our application, the “Client ID” (which identified our application to Azure) and the “Tenant ID” (a common ID which tells Azure which organisation to authenticate against). Write down these values as we will need to use them later on.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/azuread_appdetails.png&quot;&gt; &lt;img src=&quot;/images/azuread_appdetails.png&quot; alt=&quot;&quot; title=&quot;Application details after registration&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For our application we will be using the OAuth 2.0 Implicit Grant flow, as we need to access the ID tokens directly. After registering the application, we will click on ‘testapp’ and then select ‘Authentication’ from the left-side menu, scroll down to ‘Implicit grant’, tick the ‘ID tokens’ checkbox and then save so that the application is updated.&lt;/p&gt;

&lt;h3 id=&quot;define-application-roles&quot;&gt;Define application roles&lt;/h3&gt;

&lt;p&gt;Once we have the application registered with Azure, we need to let Azure know the various roles  your application supports. There is no nice and easy GUI to add the roles, instead we will edit the application manifest directly, e.g. the JSON file Azure uses to understand and register your application.&lt;/p&gt;

&lt;p&gt;On the application menu, select “Manifest” which will open the application definition and find this section:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;appRoles&quot;: [],
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For each role we need to provide the following information:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allowedMemberTypes&lt;/code&gt;: list of allowed member types, it should include a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; entry&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayName&lt;/code&gt;: The friendly name we will use when assigning roles to users&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;description&lt;/code&gt;: A brief description of what the role allows the user to do&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isEnabled&lt;/code&gt;: Set it to &lt;em&gt;true&lt;/em&gt; to enable the role&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt;: The value your application will receive inside the JWT token, when the logged in user has this role assigned, this is the important one&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt;: A unique GUID, generate something random which meets this format (hexadecimal characters): “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft provides some &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps&quot;&gt;documentation around the definition of application roles&lt;/a&gt;  you may find helpful.&lt;/p&gt;

&lt;p&gt;In our case let’s assume we have an ‘admin’ role which allows users to manage the application, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appRole&lt;/code&gt; would be filled-in with this information:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;allowedMemberTypes&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Can manage application settings&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;84ecd32a-8829-a8d8-cb4d-93dcfe8f7dab&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;isEnabled&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just add as many roles as you require to the JSON file as a list of items assigned to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appRoles&lt;/code&gt;, your file should look similar to this:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;appRoles&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;allowedMemberTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;displayName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Admin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Can manage application settings&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;84ecd32a-8829-a8d8-cb4d-93dcfe8f7dab&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isEnabled&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;admin&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;allowedMemberTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;displayName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Can edit content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;84ecd32a-8829-adda-cb4d-93dcfe8f7dac&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isEnabled&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;editor&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;allowedMemberTypes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;displayName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Reader&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Can read content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;84ecd32a-8829-9abd-cb4d-93dcfe8f7dad&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isEnabled&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;reader&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you are happy with the changes save the manifest, after verifying the syntax Azure will make the roles available for user management.&lt;/p&gt;

&lt;h2 id=&quot;assign-application-roles-to-users-or-groups&quot;&gt;Assign application roles to users or groups&lt;/h2&gt;

&lt;p&gt;To assign the roles to Azure users you will continue in the Azure AD admin centre, select “Enterprise applications” on the left pane and then click on the name of the application you created (in our case “testapp”). Then select “Users and groups” to start assigning roles.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/azuread_roles.png&quot;&gt; &lt;img src=&quot;/images/azuread_roles.png&quot; alt=&quot;&quot; title=&quot;Application user and role management&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on “Add user” and   select the user (or group) you want to assign a role to when accessing your application, then under “Select role” pick one of the role friendly names we created in the previous section, and repeat for as many roles, users and groups as needed.&lt;/p&gt;

&lt;h2 id=&quot;integrate-azuread-authentication-with-your-application&quot;&gt;Integrate AzureAD authentication with your application&lt;/h2&gt;

&lt;p&gt;Microsoft provides MSAL, a &lt;a href=&quot;https://github.com/AzureAD/microsoft-authentication-library-for-js&quot;&gt;javascript library to authenticate against AzureAD&lt;/a&gt; which has an &lt;a href=&quot;https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/README.md&quot;&gt;angular version&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My initial approach was to try and use the Angular wrapper (available in NPM as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@azure/msal-angular&lt;/code&gt;), as it makes for an easier integration - however the wrapper hides the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idToken&lt;/code&gt; property of the javascript MSAL object which is required to extract user roles which made me integrate against the MSAL.js library directly. The angular wrapper is still in preview as of this writing, but in the future it may be an option to consider.&lt;/p&gt;

&lt;p&gt;The MSAL workflow is quite simple:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Our login component will initialise the MSAL library by passing the azure client and tenant IDs&lt;/li&gt;
  &lt;li&gt;When the user clicks on the “Login with Microsoft” button we will use the MSAL object to open a pop-up which will take the user to Microsoft to authenticate&lt;/li&gt;
  &lt;li&gt;On successful authentication, Azure will redirect the user back to our application where we will use the MSAL object to read the user roles and authorise the access&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;install-and-initialise-msal&quot;&gt;Install and initialise MSAL&lt;/h3&gt;

&lt;p&gt;Start by installing the MSAL js library:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @azure/msal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the initialisation of our login component we will obtain a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserAgentApplication&lt;/code&gt; instance:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;msalConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;abcdef-0123-456789ab-cdef-012345&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// AAD Client ID&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azureLogger&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This easies MSAL debugging, check the [accompanying code](https://github.com/llmora/azuread-angular-example) for details on this property&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aad_tenant_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// To support single-tenant applications, not needed if supporting multi-tenant&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;msalConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;authority&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`https://login.microsoftonline.com/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aad_tenant_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azureADmsalInstance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserAgentApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msalConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;authenticate-the-user-against-azure&quot;&gt;Authenticate the user against Azure&lt;/h3&gt;

&lt;p&gt;When the user decides to authenticate through Azure we have two integration options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A pop-up that, after authentication, closes down and sends the results of authentication to our client-side application&lt;/li&gt;
  &lt;li&gt;A redirect to Microsoft which, after authentication, redirects back to one of the URIs we have configured when registering the application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some testing I opted for the pop-up integration due to it being easier to integrate (it requires less configuration, e.g. no redirect page to receive the response) and the impossibility to include a hash in the return URI of an AzureAD application, which makes it &lt;a href=&quot;https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/782&quot;&gt;impossible to use it with angular if you are using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashLocationStrategy&lt;/code&gt;&lt;/a&gt; (e.g. the angular paths separated with hashes, for instance &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/#/login&lt;/code&gt;). If you want to use redirect either ensure you are using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PathLocationStrategy&lt;/code&gt; or use a static HTML served outside of your angular application, as described in &lt;a href=&quot;https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/100&quot;&gt;this GitHub issue&lt;/a&gt; and configure that path in the ‘Redirect URI’ of your application registration.&lt;/p&gt;

&lt;p&gt;Going with the pop-up option is quite simple, just bind this code to the action which executes when the user selects to authenticate through Microsoft:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;loginRequest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;user.read&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azureADmsalInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;loginPopup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;loginRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[...&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;successfully&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authenticated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...]&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;p&quot;&gt;[...&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authenticating&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...]&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;troubleshooting-msal&quot;&gt;Troubleshooting MSAL&lt;/h3&gt;

&lt;p&gt;While developing with MSAL it can be a bit daunting to troubleshoot, these are some of the issues I have faced during implementation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make sure your client and tenant IDs are correct&lt;/li&gt;
  &lt;li&gt;If you are configuring the application for single-tenant you need to include the tenant ID in the authority URL&lt;/li&gt;
  &lt;li&gt;If you are using the redirect option, during development you will probably be using “localhost” as your landing page. Note that localhost and 127.0.0.1 are not the same as far as return pages are concerned (your application will work, but you will not receive the token back from Azure)&lt;/li&gt;
  &lt;li&gt;If you are getting an AADSTS700054 error (“response_type ‘id_token’ is not enabled for the application”) make sure you have selected the ‘ID token’ implicit grant, as described in the last step of &lt;a href=&quot;#register-your-application-with-azure&quot;&gt;Register your application with Azure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The first time you login to the application, an admin must consent for it to be authenticating against your AzureAD instance. Make sure an admin is the first one to login, and that they check the ‘Consent on behalf of your organisation’ when prompted:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/images/azuread_appconsent.png&quot;&gt; &lt;img src=&quot;/images/azuread_appconsent.png&quot; alt=&quot;&quot; title=&quot;Admin consent&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MSAL offers a logger that is quite verbose about the errors it encounters, if you are facing issues I strongly recommend you use the logger - see the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserAgentApplication&lt;/code&gt; instance initialisation in the &lt;a href=&quot;https://github.com/llmora/azuread-angular-example&quot;&gt;accompanying code&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2 id=&quot;access-azuread-jwt-token-and-extract-roles&quot;&gt;Access AzureAD JWT token and extract roles&lt;/h2&gt;

&lt;p&gt;After a successful login we will receive a JWT token which encodes the details of the authenticated user as well as the roles she has assigned. Use the getAccount() method of the MSAL object to retrieve the authenticated user e-mail:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azureAccount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azureADmsalInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getAccount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Retrieving the JWT token is even easier, as MSAL sets a ‘idToken’ in the response - this is the key to accessing roles as these are not exposed by MSAL directly:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;idToken&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If for whatever reason you need to use an older version of MSAL, please note that the idToken was not exposed in the response but could be extracted from the sessionStorage:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;idTokenKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idToken&lt;/code&gt; is the raw JWT token which we will use to extract the roles from, after validating it is correctly signed by the Microsoft login service to avoid login spoofing attacks. The validation of this token needs to happen on the server side, at a high-level these are the steps we need to follow:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Verify the signature, issuer, expiration and audience of the JWT token&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Extract the claims, which give us access to the following information:&lt;/p&gt;

    &lt;p&gt;&lt;em&gt;oid&lt;/em&gt;: Unique ID for the user across the Microsoft platform
&lt;em&gt;preferred_username&lt;/em&gt;: User e-mail
&lt;em&gt;roles&lt;/em&gt;: List of application roles the user has assigned&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The roles entry is a list of role strings (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; we gave to the relevant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appRoles&lt;/code&gt; in the manifest) which you can then use directly in the application to authorise the user actions.&lt;/p&gt;

&lt;p&gt;Below is a ruby implementation which extracts the user e-mail and roles from the JWT token using the &lt;a href=&quot;https://github.com/AzureAD/omniauth-azure-activedirectory/blob/master/lib/omniauth/strategies/azure_activedirectory.rb&quot;&gt;AzureActiveDirectory class&lt;/a&gt; (which does most of the heavy-lifting):&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;aad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AzureActiveDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tenant_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Verify signature, issuer and audience&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;azure_claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;azure_header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;validate_and_parse_id_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;azure_claims&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;external_identifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;azure_claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'oid'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;azure_claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'preferred_username'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;azure_claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'roles'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you have the roles you know which permissions the user has and can authorise his next actions as appropriate, as if he was a local user. Because the JWT token is only passed on in the pop-up or redirect you need to assign the roles to the user session, which will depend on how you are managing your application sessions: a local JWT, an entry in the login session, etc.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Integrating Azure AD authentication and authorisation against an angular application is quite simple if you are familiar with how these steps work:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Creating appRoles for your application in Azure&lt;/li&gt;
  &lt;li&gt;Assigning roles to users in Azure&lt;/li&gt;
  &lt;li&gt;Extracting the application roles from the Azure JWT idToken&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article and the &lt;a href=&quot;https://github.com/llmora/azuread-angular-example&quot;&gt;accompanying code&lt;/a&gt; we have shown how to implement this integration, it should be fairly easy to adapt the approach to other web frameworks besides angular as we decided to integrate the Microsoft MSAL js library directly instead of the angular wrapper.&lt;/p&gt;

&lt;p&gt;If you have comments, questions or improvements to the article please reach out on Twitter or &lt;a href=&quot;https://github.com/llmora/llmora.github.io/blob/master/_posts/2020-01-06-AngularAzureADAuthenticationAndAuthorization.md&quot;&gt;submit a pull request in GitHub&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><category term="dev" /><category term="security" /><summary type="html">There is plenty of documentation on integrating javascript applications with Microsoft cloud authentication, however there is little information on how to define which users are allowed to log-in, managing them and assigning the roles your application uses to them using Azure AD. Read on to learn how to do an end-to-end integration of Angular, Azure AD and user+role management.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/angular.jpg" /><media:content medium="image" url="http://flow.gi/angular.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">SMEG fridge shelf design and replacement</title><link href="http://flow.gi/SMEGFridgeShelves/" rel="alternate" type="text/html" title="SMEG fridge shelf design and replacement" /><published>2019-03-03T00:00:00+00:00</published><updated>2019-03-03T00:00:00+00:00</updated><id>http://flow.gi/SMEGFridgeShelves</id><content type="html" xml:base="http://flow.gi/SMEGFridgeShelves/">&lt;p&gt;The plastic shelf corners in our &lt;a href=&quot;http://www.smeg.com/&quot;&gt;SMEG&lt;/a&gt; fridge are quite flimsy and after a couple of years they just broke away. It has been impossible to source replacements so we decided to design a simple functional 3D printable piece to replace them, you can find the STL files on our &lt;a href=&quot;https://github.com/llmora/deprogramming-obsolescence/&quot;&gt;Deprogramming Obsolescence repository&lt;/a&gt; (along with editable OpenSCAD files in case you need to adjust the pieces). If you modify the designs can you share them back with the community please?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_final.png&quot;&gt; &lt;img src=&quot;/images/fridge_final.png&quot; alt=&quot;&quot; title=&quot;Fridge both parts fixed&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;back-side-shelf&quot;&gt;Back-side shelf&lt;/h2&gt;
&lt;p&gt;The piece around the back of the shelf is a bit more elaborate and is the one that tends to break more often, over half of our shelves back-side plastic corners have broken, sometime on both left and right side. This is what a healthy piece looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_backok.png&quot;&gt; &lt;img src=&quot;/images/fridge_backok.png&quot; alt=&quot;&quot; title=&quot;Fridge back part OK&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And over time, the piece at the end that goes into the side rails and holds all the weight breaks away and ends up like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_backbroken.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_backbroken.jpg&quot; alt=&quot;&quot; title=&quot;Fridge back part broken&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new  piece is a simple design, a replacement for the end that has broken away which is then attached using two screws to the old piece:&lt;/p&gt;

&lt;iframe id=&quot;vs_iframe&quot; src=&quot;https://www.viewstl.com/?embedded&amp;amp;url=https://raw.githubusercontent.com/llmora/deprogramming-obsolescence/master/smeg-fridge/smeg_back_fridge_shelf_corner.stl&amp;amp;color=gray&amp;amp;bgcolor=transparent&amp;amp;noborder=yes&quot; style=&quot;border:0;margin:0;width:100%;height:644px;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;The piece is symmetric, so you can print it to replace both the right and left corners; it should print without supports.&lt;/p&gt;

&lt;p&gt;Before you install the replacement piece you may need to remove any remaining bits of the broken piece, just try to fit the new piece and remove with a stanley cutter anything from the old piece that gets in the way.&lt;/p&gt;

&lt;p&gt;Using the 3D-printed piece as a template, drill a couple of holes through the placeholders so that the old piece plastic ends up with two holes:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_backbdrill.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_backdrill.jpg&quot; alt=&quot;&quot; title=&quot;Fridge back part drilling&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using two M3x12 screws and a self-locking M3 nut attach the replacement piece to the old piece and tighten it as this piece will support most of the weight of the shelf once installed. I did not use any glue or other chemical bonding, just the mechanical screws - but if you have any suggestions here always happy to learn.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_backfixed.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_backfixed.jpg&quot; alt=&quot;&quot; title=&quot;Fridge back part fixed&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_backfixedside.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_backfixedside.jpg&quot; alt=&quot;&quot; title=&quot;Fridge back part fixed sideview&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The shelf can now be replaced, or move on to the next section in case the front-side shelf plastic pieces are also broken.&lt;/p&gt;

&lt;h2 id=&quot;front-side-shelf&quot;&gt;Front-side shelf&lt;/h2&gt;
&lt;p&gt;The front-piece is quite straightforward, it holds the glass on two sides and works as a corner which in itself rests against the rails on the side of the fridge:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_frontok.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_frontok.jpg&quot; alt=&quot;&quot; title=&quot;Fridge front part OK&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case the whole corner of the piece just broke out, leaving the piece with no support at the front. The design is again quite simple, just a corner with a grove for the glass to slide in:&lt;/p&gt;

&lt;iframe id=&quot;vs_iframe&quot; src=&quot;https://www.viewstl.com/?embedded&amp;amp;url=https://raw.githubusercontent.com/llmora/deprogramming-obsolescence/master/smeg-fridge/smeg_front_fridge_shelf_corner.stl&amp;amp;color=gray&amp;amp;bgcolor=transparent&amp;amp;noborder=yes&quot; style=&quot;border:0;margin:0;width:100%;height:644px;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;If you print the design in the original STL orientation you should have no need to use supports, however given the height and the relatively low surface you need really good first layer adherence.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_frontprinted.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_frontprinted.jpg&quot; alt=&quot;&quot; title=&quot;Fridge front part printed&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the piece is not glued the glass is a snug fit, you will need to use quite a but of force to insert the glass in - I suggest using a rubber hammer or, if you do not have one handy, just fit in the piece on the glass, then hit the whole assembly until the glass falls into place. Because this is a direct replacement for a part of the front-side, you will need to cut the old plastic part with a stanley knife so that the new piece fits in, the piece measures 8.5cm long:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/fridge_frontfit.jpg&quot;&gt; &lt;img src=&quot;/images/fridge_frontfit.jpg&quot; alt=&quot;&quot; title=&quot;Fridge front part fitted in&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have used the pieces for the past month and it seems as if they are going to extend the life of the shelves for at least a couple of years, enjoy the pieces if you use them - and if you make any adjustments please share back on github so that everyone can benefit, let’s continue fighting obsolescence!&lt;/p&gt;</content><author><name></name></author><category term="making" /><summary type="html">The plastic shelf corners in our SMEG fridge are quite flimsy and after a couple of years they just broke away. It has been impossible to source replacements so we decided to design a simple functional 3D printable piece to replace them, you can find the STL files on our Deprogramming Obsolescence repository (along with editable OpenSCAD files in case you need to adjust the pieces). If you modify the designs can you share them back with the community please?</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/fridge.jpg" /><media:content medium="image" url="http://flow.gi/fridge.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Installing a new Industrias Lorenzo Eurojoystick 2</title><link href="http://flow.gi/IndustriasLorenzoEurojoystick2/" rel="alternate" type="text/html" title="Installing a new Industrias Lorenzo Eurojoystick 2" /><published>2019-02-24T00:00:00+00:00</published><updated>2019-02-24T00:00:00+00:00</updated><id>http://flow.gi/IndustriasLorenzoEurojoystick2</id><content type="html" xml:base="http://flow.gi/IndustriasLorenzoEurojoystick2/">&lt;p&gt;After &lt;a href=&quot;/HappSuperJoystickActuator/&quot;&gt;replacing the actuator on our Happ Super joystick&lt;/a&gt; gameplay has been much better, however the microswitches are showing their age so I ordered a pair of &lt;a href=&quot;https://www.arcadexpress.com/joysticks-arcade/39-361-eurojoystick-2-de-industrias-lorenzo.html&quot;&gt;Industrias Lorenzo Eurojoystick 2&lt;/a&gt; for 18 EUR a piece.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_unboxing.jpg&quot;&gt; &lt;img src=&quot;/images/il_unboxing.jpg&quot; alt=&quot;&quot; title=&quot;A pair of Industrias Lorenzo Eurojoystick 2&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These joysticks come with a trademark square actuator, which makes hitting diagonals much easier and high-quality Cherry D44X microswitches.&lt;/p&gt;

&lt;p&gt;Replacing the joystick in an arcade cabinet is not really a major operation, however I could not find step-by-step instructions anywhere so I am documenting it here in case someone else benefits from it.&lt;/p&gt;

&lt;h2 id=&quot;removing-the-old-joystick&quot;&gt;Removing the old joystick&lt;/h2&gt;
&lt;p&gt;Before you start take a picture of the cabling of the current joystick, making sure you can clearly identify the up/down/right/left connectors - this will ease up the reconnection of the cables once the new joystick is installed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_cabling.jpg&quot;&gt; &lt;img src=&quot;/images/il_cabling.jpg&quot; alt=&quot;&quot; title=&quot;Capture the current cabling before unplugging&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you capture how each cable is attached - for each microswitch there will be a “ground” cable which is typically daisy-chained across the four microswitches and a “signal” cable. Each microswitch may have 2 or 3 connectors, note which one is the “ground” (COM) and which one is the “signal”: for the “signal” you will typically have two options, make sure you connect it to the “Normally Open” (NO) connector, which is usually pictured as an open circuit.&lt;/p&gt;

&lt;p&gt;While the joystick is still secured to the control panel we will remove the shaft. Use a small flat screwdriver and needle-nose pliers to ease the shaft “e-clip”, make sure you do not lose this piece.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_old_components.jpg&quot;&gt; &lt;img src=&quot;/images/il_old_components.jpg&quot; alt=&quot;&quot; title=&quot;Removing the shaft&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the “e-clip” is removed, pull the actuator away (in the pictures the &lt;a href=&quot;/HappSuperJoystickActuator/&quot;&gt;tight actuator we built&lt;/a&gt;, not the original one) and then pull the shaft which should slide out without any problem.&lt;/p&gt;

&lt;p&gt;Once you are satisfied the picture is good enough, unplug the eight connectors that go to the microswitches.&lt;/p&gt;

&lt;p&gt;After that remove the four screws holding the joystick base attached to the control panel, which frees up the base.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_old_joystick.jpg&quot;&gt; &lt;img src=&quot;/images/il_old_joystick.jpg&quot; alt=&quot;&quot; title=&quot;Capture the current cabling before unplugging&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Put together the old joystick (base, shaft, actuator and “e-clip”) so that you can put it away and use for future projects after some TLC (we will likely replace the aging microswitches before usign them again).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_old_pack.jpg&quot;&gt; &lt;img src=&quot;/images/il_old_pack.jpg&quot; alt=&quot;&quot; title=&quot;Happ Super joysticks ready to be packed&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;installing-the-new-joystick&quot;&gt;Installing the new joystick&lt;/h2&gt;
&lt;p&gt;Now that we have the old joysticks out of the way it may be a good opportunity to clean the control panel, after all you only change the joysticks once every generation.&lt;/p&gt;

&lt;p&gt;Ensure that the new joystick mounting holes align with those of the old joystick we removed; in our case the Happ Super and the IL Eurojoystick 2 have exactly the same mounting holes (I guess there is a standard size for joystick mounts).&lt;/p&gt;

&lt;p&gt;Remove the “e-clip” and actuator from the new joystick; the shaft should slide out easily after that.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_new_shaft.jpg&quot;&gt; &lt;img src=&quot;/images/il_new_shaft.jpg&quot; alt=&quot;&quot; title=&quot;IL Eurojoystick 2 disassembled shaft&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fit the base on the arcade control panel, using the same screws you removed with the old joystick.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_newbase.jpg&quot;&gt; &lt;img src=&quot;/images/il_newbase.jpg&quot; alt=&quot;&quot; title=&quot;IL Eurojoystick 2 base installed but still unplugged&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reinstall the shaft and actuator, and secure both pieces with the “e-clip”.&lt;/p&gt;

&lt;p&gt;As a last step, and using the picture you took at the beginning of the process, re-connect the eight cables to the microswitches, ensuring that the “signal” cable is connected to the “Normally Open” (NO) connector. Before you close the cabinet I recommend you test the joystick is working as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_connected.jpg&quot;&gt; &lt;img src=&quot;/images/il_connected.jpg&quot; alt=&quot;&quot; title=&quot;IE Eurojoystick 2 base installed but still unplugged&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Close the cabinet and enjoy your new joysticks, they should last for another twenty years. The IL Eurojoystick2 feel really tight, tighter than our “modified” Happ Super and really responsive. I mostly use it for shot’em ups so I am really happy with the change, if you like other type of games your mileage may vary.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/il_new_installed.jpg&quot;&gt; &lt;img src=&quot;/images/il_new_installed.jpg&quot; alt=&quot;&quot; title=&quot;Capture the current cabling before unplugging&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><category term="arcade" /><summary type="html">After replacing the actuator on our Happ Super joystick gameplay has been much better, however the microswitches are showing their age so I ordered a pair of Industrias Lorenzo Eurojoystick 2 for 18 EUR a piece.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://flow.gi/eurojoystick2.jpg" /><media:content medium="image" url="http://flow.gi/eurojoystick2.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>