2024-10-21 12:12:28 +00:00
|
|
|
#import "@preview/ichigo:0.1.0": config, prob
|
2024-10-22 11:12:10 +00:00
|
|
|
#import "@preview/algorithmic:0.1.0"
|
|
|
|
#import algorithmic: algorithm
|
2024-10-22 13:11:53 +00:00
|
|
|
#import "@preview/fletcher:0.5.1" as fletcher: diagram, node, edge
|
|
|
|
#import fletcher.shapes: house, hexagon
|
|
|
|
|
|
|
|
#let blob(pos, label, tint: white, ..args) = node(
|
|
|
|
pos, align(center, label),
|
|
|
|
width: 26mm,
|
|
|
|
fill: tint.lighten(60%),
|
|
|
|
stroke: 1pt + tint.darken(20%),
|
|
|
|
corner-radius: 5pt,
|
|
|
|
..args,
|
|
|
|
)
|
|
|
|
|
2024-10-21 12:12:28 +00:00
|
|
|
|
|
|
|
#show: config.with(
|
|
|
|
course-name: "SMART CARDS & NFC",
|
|
|
|
serial-str: "k12104785",
|
|
|
|
author-info: [
|
|
|
|
Lukas Heiligenbrunner
|
|
|
|
],
|
|
|
|
author-names: "Lukas Heiligenbrunner",
|
|
|
|
)
|
|
|
|
|
|
|
|
#prob[
|
|
|
|
#figure(
|
|
|
|
image("Screenshot From 2024-10-21 14-07-41.png", width: 80%),
|
|
|
|
caption: [
|
|
|
|
Pin try counter.
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
][
|
2024-10-22 13:11:53 +00:00
|
|
|
- The PIN_TRY_COUNTER is prone to turn off attacks.
|
2024-10-22 10:08:07 +00:00
|
|
|
Each time the chip resets the ram value of the counter is cleared and one gets basically infinite retries.
|
2024-10-22 11:12:10 +00:00
|
|
|
|
|
|
|
Solution:
|
|
|
|
- Store counter in non-volatile memory eg. EEPROM, flash, eMMC.
|
|
|
|
- Store counter in a secure server backend.
|
|
|
|
|
2024-10-22 10:08:07 +00:00
|
|
|
- Depending on the implementation of the comparison operation, it might leak side-channel information.
|
|
|
|
For example, if the comparison is done byte-wise, the attacker can determine the correct byte by comparing the time it takes to compare the bytes.
|
2024-10-22 11:12:10 +00:00
|
|
|
|
|
|
|
Solution:
|
|
|
|
- Implement a constant time comparison operation.
|
2024-10-22 13:11:53 +00:00
|
|
|
|
|
|
|
- PIN_TRY_COUNTER is incremented after the comparison operation.
|
|
|
|
If the operation is interrupted due to a non atomic operation, the counter not incremented.
|
|
|
|
|
|
|
|
Solution:
|
|
|
|
- Do counter++ before comparison. This way the attacker can't determine if the counter is incremented or not.
|
|
|
|
|
|
|
|
#sym.arrow.r See flowchart
|
|
|
|
- Implement a atomic operation for the counter incrementation.
|
|
|
|
|
|
|
|
#align(center)[
|
|
|
|
#diagram(
|
|
|
|
spacing: 8pt,
|
|
|
|
cell-size: (8mm, 10mm),
|
|
|
|
edge-stroke: 1pt,
|
|
|
|
edge-corner-radius: 5pt,
|
|
|
|
mark-scale: 70%,
|
|
|
|
debug: false,
|
|
|
|
|
|
|
|
blob((2,0), [PIN Verification], tint: yellow, shape: fletcher.shapes.pill),
|
|
|
|
edge("-|>"),
|
|
|
|
blob((2,1), [PIN_Try_Counter < LIMIT], tint: green, shape: fletcher.shapes.hexagon, width: 35mm),
|
|
|
|
edge("ll,dddd", "-|>", label: "No"),
|
|
|
|
edge("-|>", "d"),
|
|
|
|
blob((0,5), [Result: Card/Pin blocked], tint: yellow, shape: fletcher.shapes.pill),
|
|
|
|
|
|
|
|
blob((2,2), [Pin_try_counter++], tint: blue, shape: fletcher.shapes.rect, width: auto),
|
|
|
|
edge("-|>"),
|
|
|
|
blob((2,3), [PIN == Ref_PIN?], tint: green, shape: fletcher.shapes.hexagon, width: auto),
|
|
|
|
edge("l,d", "-|>", label: "Yes"),
|
|
|
|
edge( "r,dd", "-|>", label: "No"),
|
|
|
|
blob((1,4), [Pin_try_counter = 0], tint: blue, shape: fletcher.shapes.rect, width: auto),
|
|
|
|
edge("-|>"),
|
|
|
|
blob((1,5), [PIN Verification], tint: yellow, shape: fletcher.shapes.pill),
|
|
|
|
blob((3,5), [PIN Verification], tint: yellow, shape: fletcher.shapes.pill),
|
|
|
|
)
|
|
|
|
]
|
2024-10-21 12:12:28 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
#prob[
|
|
|
|
#figure(
|
|
|
|
image("Screenshot From 2024-10-21 14-10-15.png", width: 80%),
|
|
|
|
caption: [
|
|
|
|
Pin comparision (PIN == REF_PIN).
|
|
|
|
],
|
|
|
|
)
|
|
|
|
][
|
2024-10-22 11:12:10 +00:00
|
|
|
- The comparison of the entered pin and the reference pin is array entry wise.
|
|
|
|
If a entry doesn't match the comparison is short-handed and the function returns no match.
|
|
|
|
This is prone to a timing side-channel attack. If a pin digit matches the comparison takes longer than if it doesn't.
|
|
|
|
|
|
|
|
Solution:
|
|
|
|
- Implement a constant time comparison operation. (no comparison shorthand)
|
|
|
|
|
|
|
|
For example:
|
|
|
|
#algorithm({
|
|
|
|
import algorithmic: *
|
|
|
|
Function("Constant-Time-Compare", args: ("PIN", "Ref_PIN"), {
|
|
|
|
Cmt[Check if lengths are equal]
|
|
|
|
If(cond: $"length" ("PIN") != "length"("Ref_PIN")$, {
|
|
|
|
Return[false]
|
|
|
|
})
|
|
|
|
State[]
|
|
|
|
Cmt[Initialize result variable to 0]
|
|
|
|
Assign[$"result"$][$0$]
|
|
|
|
State[]
|
|
|
|
Cmt[Loop through each character in PIN and Ref_PIN]
|
|
|
|
For(cond: [$i=0$; $i < "length"("PIN") - 1$], {
|
|
|
|
Cmt[XOR corresponding characters and accumulate result]
|
|
|
|
Assign[$"result"$][$"result" or ("PIN"[i] xor "Ref_PIN"[i])$]
|
|
|
|
})
|
|
|
|
State[]
|
|
|
|
Cmt[Return true if result is 0, else false]
|
|
|
|
Return[$"result" == 0$]
|
|
|
|
})
|
|
|
|
})
|
2024-10-21 12:12:28 +00:00
|
|
|
]
|