From c5bc76dd1ce9eb3e6354aaa89913e9371233091f Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 15 Nov 2024 15:46:51 -0500 Subject: [PATCH 01/25] diceware: add short list v2, requiring 4 dices and providing longer words then short list v1 for easier to remember passphrases This lists comes from https://www.eff.org/files/2016/09/08/eff_short_wordlist_2_0.txt Refered in article: https://www.eff.org/dice Signed-off-by: Thierry Laurion --- .../eff_short_wordlist_2_0.txt | 1296 +++++++++++++++++ 1 file changed, 1296 insertions(+) create mode 100644 initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt diff --git a/initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt b/initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt new file mode 100644 index 000000000..ae09babe4 --- /dev/null +++ b/initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt @@ -0,0 +1,1296 @@ +1111 aardvark +1112 abandoned +1113 abbreviate +1114 abdomen +1115 abhorrence +1116 abiding +1121 abnormal +1122 abrasion +1123 absorbing +1124 abundant +1125 abyss +1126 academy +1131 accountant +1132 acetone +1133 achiness +1134 acid +1135 acoustics +1136 acquire +1141 acrobat +1142 actress +1143 acuteness +1144 aerosol +1145 aesthetic +1146 affidavit +1151 afloat +1152 afraid +1153 aftershave +1154 again +1155 agency +1156 aggressor +1161 aghast +1162 agitate +1163 agnostic +1164 agonizing +1165 agreeing +1166 aidless +1211 aimlessly +1212 ajar +1213 alarmclock +1214 albatross +1215 alchemy +1216 alfalfa +1221 algae +1222 aliens +1223 alkaline +1224 almanac +1225 alongside +1226 alphabet +1231 already +1232 also +1233 altitude +1234 aluminum +1235 always +1236 amazingly +1241 ambulance +1242 amendment +1243 amiable +1244 ammunition +1245 amnesty +1246 amoeba +1251 amplifier +1252 amuser +1253 anagram +1254 anchor +1255 android +1256 anesthesia +1261 angelfish +1262 animal +1263 anklet +1264 announcer +1265 anonymous +1266 answer +1311 antelope +1312 anxiety +1313 anyplace +1314 aorta +1315 apartment +1316 apnea +1321 apostrophe +1322 apple +1323 apricot +1324 aquamarine +1325 arachnid +1326 arbitrate +1331 ardently +1332 arena +1333 argument +1334 aristocrat +1335 armchair +1336 aromatic +1341 arrowhead +1342 arsonist +1343 artichoke +1344 asbestos +1345 ascend +1346 aseptic +1351 ashamed +1352 asinine +1353 asleep +1354 asocial +1355 asparagus +1356 astronaut +1361 asymmetric +1362 atlas +1363 atmosphere +1364 atom +1365 atrocious +1366 attic +1411 atypical +1412 auctioneer +1413 auditorium +1414 augmented +1415 auspicious +1416 automobile +1421 auxiliary +1422 avalanche +1423 avenue +1424 aviator +1425 avocado +1426 awareness +1431 awhile +1432 awkward +1433 awning +1434 awoke +1435 axially +1436 azalea +1441 babbling +1442 backpack +1443 badass +1444 bagpipe +1445 bakery +1446 balancing +1451 bamboo +1452 banana +1453 barracuda +1454 basket +1455 bathrobe +1456 bazooka +1461 blade +1462 blender +1463 blimp +1464 blouse +1465 blurred +1466 boatyard +1511 bobcat +1512 body +1513 bogusness +1514 bohemian +1515 boiler +1516 bonnet +1521 boots +1522 borough +1523 bossiness +1524 bottle +1525 bouquet +1526 boxlike +1531 breath +1532 briefcase +1533 broom +1534 brushes +1535 bubblegum +1536 buckle +1541 buddhist +1542 buffalo +1543 bullfrog +1544 bunny +1545 busboy +1546 buzzard +1551 cabin +1552 cactus +1553 cadillac +1554 cafeteria +1555 cage +1556 cahoots +1561 cajoling +1562 cakewalk +1563 calculator +1564 camera +1565 canister +1566 capsule +1611 carrot +1612 cashew +1613 cathedral +1614 caucasian +1615 caviar +1616 ceasefire +1621 cedar +1622 celery +1623 cement +1624 census +1625 ceramics +1626 cesspool +1631 chalkboard +1632 cheesecake +1633 chimney +1634 chlorine +1635 chopsticks +1636 chrome +1641 chute +1642 cilantro +1643 cinnamon +1644 circle +1645 cityscape +1646 civilian +1651 clay +1652 clergyman +1653 clipboard +1654 clock +1655 clubhouse +1656 coathanger +1661 cobweb +1662 coconut +1663 codeword +1664 coexistent +1665 coffeecake +1666 cognitive +2111 cohabitate +2112 collarbone +2113 computer +2114 confetti +2115 copier +2116 cornea +2121 cosmetics +2122 cotton +2123 couch +2124 coverless +2125 coyote +2126 coziness +2131 crawfish +2132 crewmember +2133 crib +2134 croissant +2135 crumble +2136 crystal +2141 cubical +2142 cucumber +2143 cuddly +2144 cufflink +2145 cuisine +2146 culprit +2151 cup +2152 curry +2153 cushion +2154 cuticle +2155 cybernetic +2156 cyclist +2161 cylinder +2162 cymbal +2163 cynicism +2164 cypress +2165 cytoplasm +2166 dachshund +2211 daffodil +2212 dagger +2213 dairy +2214 dalmatian +2215 dandelion +2216 dartboard +2221 dastardly +2222 datebook +2223 daughter +2224 dawn +2225 daytime +2226 dazzler +2231 dealer +2232 debris +2233 decal +2234 dedicate +2235 deepness +2236 defrost +2241 degree +2242 dehydrator +2243 deliverer +2244 democrat +2245 dentist +2246 deodorant +2251 depot +2252 deranged +2253 desktop +2254 detergent +2255 device +2256 dexterity +2261 diamond +2262 dibs +2263 dictionary +2264 diffuser +2265 digit +2266 dilated +2311 dimple +2312 dinnerware +2313 dioxide +2314 diploma +2315 directory +2316 dishcloth +2321 ditto +2322 dividers +2323 dizziness +2324 doctor +2325 dodge +2326 doll +2331 dominoes +2332 donut +2333 doorstep +2334 dorsal +2335 double +2336 downstairs +2341 dozed +2342 drainpipe +2343 dresser +2344 driftwood +2345 droppings +2346 drum +2351 dryer +2352 dubiously +2353 duckling +2354 duffel +2355 dugout +2356 dumpster +2361 duplex +2362 durable +2363 dustpan +2364 dutiful +2365 duvet +2366 dwarfism +2411 dwelling +2412 dwindling +2413 dynamite +2414 dyslexia +2415 eagerness +2416 earlobe +2421 easel +2422 eavesdrop +2423 ebook +2424 eccentric +2425 echoless +2426 eclipse +2431 ecosystem +2432 ecstasy +2433 edged +2434 editor +2435 educator +2436 eelworm +2441 eerie +2442 effects +2443 eggnog +2444 egomaniac +2445 ejection +2446 elastic +2451 elbow +2452 elderly +2453 elephant +2454 elfishly +2455 eliminator +2456 elk +2461 elliptical +2462 elongated +2463 elsewhere +2464 elusive +2465 elves +2466 emancipate +2511 embroidery +2512 emcee +2513 emerald +2514 emission +2515 emoticon +2516 emperor +2521 emulate +2522 enactment +2523 enchilada +2524 endorphin +2525 energy +2526 enforcer +2531 engine +2532 enhance +2533 enigmatic +2534 enjoyably +2535 enlarged +2536 enormous +2541 enquirer +2542 enrollment +2543 ensemble +2544 entryway +2545 enunciate +2546 envoy +2551 enzyme +2552 epidemic +2553 equipment +2554 erasable +2555 ergonomic +2556 erratic +2561 eruption +2562 escalator +2563 eskimo +2564 esophagus +2565 espresso +2566 essay +2611 estrogen +2612 etching +2613 eternal +2614 ethics +2615 etiquette +2616 eucalyptus +2621 eulogy +2622 euphemism +2623 euthanize +2624 evacuation +2625 evergreen +2626 evidence +2631 evolution +2632 exam +2633 excerpt +2634 exerciser +2635 exfoliate +2636 exhale +2641 exist +2642 exorcist +2643 explode +2644 exquisite +2645 exterior +2646 exuberant +2651 fabric +2652 factory +2653 faded +2654 failsafe +2655 falcon +2656 family +2661 fanfare +2662 fasten +2663 faucet +2664 favorite +2665 feasibly +2666 february +3111 federal +3112 feedback +3113 feigned +3114 feline +3115 femur +3116 fence +3121 ferret +3122 festival +3123 fettuccine +3124 feudalist +3125 feverish +3126 fiberglass +3131 fictitious +3132 fiddle +3133 figurine +3134 fillet +3135 finalist +3136 fiscally +3141 fixture +3142 flashlight +3143 fleshiness +3144 flight +3145 florist +3146 flypaper +3151 foamless +3152 focus +3153 foggy +3154 folksong +3155 fondue +3156 footpath +3161 fossil +3162 fountain +3163 fox +3164 fragment +3165 freeway +3166 fridge +3211 frosting +3212 fruit +3213 fryingpan +3214 gadget +3215 gainfully +3216 gallstone +3221 gamekeeper +3222 gangway +3223 garlic +3224 gaslight +3225 gathering +3226 gauntlet +3231 gearbox +3232 gecko +3233 gem +3234 generator +3235 geographer +3236 gerbil +3241 gesture +3242 getaway +3243 geyser +3244 ghoulishly +3245 gibberish +3246 giddiness +3251 giftshop +3252 gigabyte +3253 gimmick +3254 giraffe +3255 giveaway +3256 gizmo +3261 glasses +3262 gleeful +3263 glisten +3264 glove +3265 glucose +3266 glycerin +3311 gnarly +3312 gnomish +3313 goatskin +3314 goggles +3315 goldfish +3316 gong +3321 gooey +3322 gorgeous +3323 gosling +3324 gothic +3325 gourmet +3326 governor +3331 grape +3332 greyhound +3333 grill +3334 groundhog +3335 grumbling +3336 guacamole +3341 guerrilla +3342 guitar +3343 gullible +3344 gumdrop +3345 gurgling +3346 gusto +3351 gutless +3352 gymnast +3353 gynecology +3354 gyration +3355 habitat +3356 hacking +3361 haggard +3362 haiku +3363 halogen +3364 hamburger +3365 handgun +3366 happiness +3411 hardhat +3412 hastily +3413 hatchling +3414 haughty +3415 hazelnut +3416 headband +3421 hedgehog +3422 hefty +3423 heinously +3424 helmet +3425 hemoglobin +3426 henceforth +3431 herbs +3432 hesitation +3433 hexagon +3434 hubcap +3435 huddling +3436 huff +3441 hugeness +3442 hullabaloo +3443 human +3444 hunter +3445 hurricane +3446 hushing +3451 hyacinth +3452 hybrid +3453 hydrant +3454 hygienist +3455 hypnotist +3456 ibuprofen +3461 icepack +3462 icing +3463 iconic +3464 identical +3465 idiocy +3466 idly +3511 igloo +3512 ignition +3513 iguana +3514 illuminate +3515 imaging +3516 imbecile +3521 imitator +3522 immigrant +3523 imprint +3524 iodine +3525 ionosphere +3526 ipad +3531 iphone +3532 iridescent +3533 irksome +3534 iron +3535 irrigation +3536 island +3541 isotope +3542 issueless +3543 italicize +3544 itemizer +3545 itinerary +3546 itunes +3551 ivory +3552 jabbering +3553 jackrabbit +3554 jaguar +3555 jailhouse +3556 jalapeno +3561 jamboree +3562 janitor +3563 jarring +3564 jasmine +3565 jaundice +3566 jawbreaker +3611 jaywalker +3612 jazz +3613 jealous +3614 jeep +3615 jelly +3616 jeopardize +3621 jersey +3622 jetski +3623 jezebel +3624 jiffy +3625 jigsaw +3626 jingling +3631 jobholder +3632 jockstrap +3633 jogging +3634 john +3635 joinable +3636 jokingly +3641 journal +3642 jovial +3643 joystick +3644 jubilant +3645 judiciary +3646 juggle +3651 juice +3652 jujitsu +3653 jukebox +3654 jumpiness +3655 junkyard +3656 juror +3661 justifying +3662 juvenile +3663 kabob +3664 kamikaze +3665 kangaroo +3666 karate +4111 kayak +4112 keepsake +4113 kennel +4114 kerosene +4115 ketchup +4116 khaki +4121 kickstand +4122 kilogram +4123 kimono +4124 kingdom +4125 kiosk +4126 kissing +4131 kite +4132 kleenex +4133 knapsack +4134 kneecap +4135 knickers +4136 koala +4141 krypton +4142 laboratory +4143 ladder +4144 lakefront +4145 lantern +4146 laptop +4151 laryngitis +4152 lasagna +4153 latch +4154 laundry +4155 lavender +4156 laxative +4161 lazybones +4162 lecturer +4163 leftover +4164 leggings +4165 leisure +4166 lemon +4211 length +4212 leopard +4213 leprechaun +4214 lettuce +4215 leukemia +4216 levers +4221 lewdness +4222 liability +4223 library +4224 licorice +4225 lifeboat +4226 lightbulb +4231 likewise +4232 lilac +4233 limousine +4234 lint +4235 lioness +4236 lipstick +4241 liquid +4242 listless +4243 litter +4244 liverwurst +4245 lizard +4246 llama +4251 luau +4252 lubricant +4253 lucidity +4254 ludicrous +4255 luggage +4256 lukewarm +4261 lullaby +4262 lumberjack +4263 lunchbox +4264 luridness +4265 luscious +4266 luxurious +4311 lyrics +4312 macaroni +4313 maestro +4314 magazine +4315 mahogany +4316 maimed +4321 majority +4322 makeover +4323 malformed +4324 mammal +4325 mango +4326 mapmaker +4331 marbles +4332 massager +4333 matchstick +4334 maverick +4335 maximum +4336 mayonnaise +4341 moaning +4342 mobilize +4343 moccasin +4344 modify +4345 moisture +4346 molecule +4351 momentum +4352 monastery +4353 moonshine +4354 mortuary +4355 mosquito +4356 motorcycle +4361 mousetrap +4362 movie +4363 mower +4364 mozzarella +4365 muckiness +4366 mudflow +4411 mugshot +4412 mule +4413 mummy +4414 mundane +4415 muppet +4416 mural +4421 mustard +4422 mutation +4423 myriad +4424 myspace +4425 myth +4426 nail +4431 namesake +4432 nanosecond +4433 napkin +4434 narrator +4435 nastiness +4436 natives +4441 nautically +4442 navigate +4443 nearest +4444 nebula +4445 nectar +4446 nefarious +4451 negotiator +4452 neither +4453 nemesis +4454 neoliberal +4455 nephew +4456 nervously +4461 nest +4462 netting +4463 neuron +4464 nevermore +4465 nextdoor +4466 nicotine +4511 niece +4512 nimbleness +4513 nintendo +4514 nirvana +4515 nuclear +4516 nugget +4521 nuisance +4522 nullify +4523 numbing +4524 nuptials +4525 nursery +4526 nutcracker +4531 nylon +4532 oasis +4533 oat +4534 obediently +4535 obituary +4536 object +4541 obliterate +4542 obnoxious +4543 observer +4544 obtain +4545 obvious +4546 occupation +4551 oceanic +4552 octopus +4553 ocular +4554 office +4555 oftentimes +4556 oiliness +4561 ointment +4562 older +4563 olympics +4564 omissible +4565 omnivorous +4566 oncoming +4611 onion +4612 onlooker +4613 onstage +4614 onward +4615 onyx +4616 oomph +4621 opaquely +4622 opera +4623 opium +4624 opossum +4625 opponent +4626 optical +4631 opulently +4632 oscillator +4633 osmosis +4634 ostrich +4635 otherwise +4636 ought +4641 outhouse +4642 ovation +4643 oven +4644 owlish +4645 oxford +4646 oxidize +4651 oxygen +4652 oyster +4653 ozone +4654 pacemaker +4655 padlock +4656 pageant +4661 pajamas +4662 palm +4663 pamphlet +4664 pantyhose +4665 paprika +4666 parakeet +5111 passport +5112 patio +5113 pauper +5114 pavement +5115 payphone +5116 pebble +5121 peculiarly +5122 pedometer +5123 pegboard +5124 pelican +5125 penguin +5126 peony +5131 pepperoni +5132 peroxide +5133 pesticide +5134 petroleum +5135 pewter +5136 pharmacy +5141 pheasant +5142 phonebook +5143 phrasing +5144 physician +5145 plank +5146 pledge +5151 plotted +5152 plug +5153 plywood +5154 pneumonia +5155 podiatrist +5156 poetic +5161 pogo +5162 poison +5163 poking +5164 policeman +5165 poncho +5166 popcorn +5211 porcupine +5212 postcard +5213 poultry +5214 powerboat +5215 prairie +5216 pretzel +5221 princess +5222 propeller +5223 prune +5224 pry +5225 pseudo +5226 psychopath +5231 publisher +5232 pucker +5233 pueblo +5234 pulley +5235 pumpkin +5236 punchbowl +5241 puppy +5242 purse +5243 pushup +5244 putt +5245 puzzle +5246 pyramid +5251 python +5252 quarters +5253 quesadilla +5254 quilt +5255 quote +5256 racoon +5261 radish +5262 ragweed +5263 railroad +5264 rampantly +5265 rancidity +5266 rarity +5311 raspberry +5312 ravishing +5313 rearrange +5314 rebuilt +5315 receipt +5316 reentry +5321 refinery +5322 register +5323 rehydrate +5324 reimburse +5325 rejoicing +5326 rekindle +5331 relic +5332 remote +5333 renovator +5334 reopen +5335 reporter +5336 request +5341 rerun +5342 reservoir +5343 retriever +5344 reunion +5345 revolver +5346 rewrite +5351 rhapsody +5352 rhetoric +5353 rhino +5354 rhubarb +5355 rhyme +5356 ribbon +5361 riches +5362 ridden +5363 rigidness +5364 rimmed +5365 riptide +5366 riskily +5411 ritzy +5412 riverboat +5413 roamer +5414 robe +5415 rocket +5416 romancer +5421 ropelike +5422 rotisserie +5423 roundtable +5424 royal +5425 rubber +5426 rudderless +5431 rugby +5432 ruined +5433 rulebook +5434 rummage +5435 running +5436 rupture +5441 rustproof +5442 sabotage +5443 sacrifice +5444 saddlebag +5445 saffron +5446 sainthood +5451 saltshaker +5452 samurai +5453 sandworm +5454 sapphire +5455 sardine +5456 sassy +5461 satchel +5462 sauna +5463 savage +5464 saxophone +5465 scarf +5466 scenario +5511 schoolbook +5512 scientist +5513 scooter +5514 scrapbook +5515 sculpture +5516 scythe +5521 secretary +5522 sedative +5523 segregator +5524 seismology +5525 selected +5526 semicolon +5531 senator +5532 septum +5533 sequence +5534 serpent +5535 sesame +5536 settler +5541 severely +5542 shack +5543 shelf +5544 shirt +5545 shovel +5546 shrimp +5551 shuttle +5552 shyness +5553 siamese +5554 sibling +5555 siesta +5556 silicon +5561 simmering +5562 singles +5563 sisterhood +5564 sitcom +5565 sixfold +5566 sizable +5611 skateboard +5612 skeleton +5613 skies +5614 skulk +5615 skylight +5616 slapping +5621 sled +5622 slingshot +5623 sloth +5624 slumbering +5625 smartphone +5626 smelliness +5631 smitten +5632 smokestack +5633 smudge +5634 snapshot +5635 sneezing +5636 sniff +5641 snowsuit +5642 snugness +5643 speakers +5644 sphinx +5645 spider +5646 splashing +5651 sponge +5652 sprout +5653 spur +5654 spyglass +5655 squirrel +5656 statue +5661 steamboat +5662 stingray +5663 stopwatch +5664 strawberry +5665 student +5666 stylus +6111 suave +6112 subway +6113 suction +6114 suds +6115 suffocate +6116 sugar +6121 suitcase +6122 sulphur +6123 superstore +6124 surfer +6125 sushi +6126 swan +6131 sweatshirt +6132 swimwear +6133 sword +6134 sycamore +6135 syllable +6136 symphony +6141 synagogue +6142 syringes +6143 systemize +6144 tablespoon +6145 taco +6146 tadpole +6151 taekwondo +6152 tagalong +6153 takeout +6154 tallness +6155 tamale +6156 tanned +6161 tapestry +6162 tarantula +6163 tastebud +6164 tattoo +6165 tavern +6166 thaw +6211 theater +6212 thimble +6213 thorn +6214 throat +6215 thumb +6216 thwarting +6221 tiara +6222 tidbit +6223 tiebreaker +6224 tiger +6225 timid +6226 tinsel +6231 tiptoeing +6232 tirade +6233 tissue +6234 tractor +6235 tree +6236 tripod +6241 trousers +6242 trucks +6243 tryout +6244 tubeless +6245 tuesday +6246 tugboat +6251 tulip +6252 tumbleweed +6253 tupperware +6254 turtle +6255 tusk +6256 tutorial +6261 tuxedo +6262 tweezers +6263 twins +6264 tyrannical +6265 ultrasound +6266 umbrella +6311 umpire +6312 unarmored +6313 unbuttoned +6314 uncle +6315 underwear +6316 unevenness +6321 unflavored +6322 ungloved +6323 unhinge +6324 unicycle +6325 unjustly +6326 unknown +6331 unlocking +6332 unmarked +6333 unnoticed +6334 unopened +6335 unpaved +6336 unquenched +6341 unroll +6342 unscrewing +6343 untied +6344 unusual +6345 unveiled +6346 unwrinkled +6351 unyielding +6352 unzip +6353 upbeat +6354 upcountry +6355 update +6356 upfront +6361 upgrade +6362 upholstery +6363 upkeep +6364 upload +6365 uppercut +6366 upright +6411 upstairs +6412 uptown +6413 upwind +6414 uranium +6415 urban +6416 urchin +6421 urethane +6422 urgent +6423 urologist +6424 username +6425 usher +6426 utensil +6431 utility +6432 utmost +6433 utopia +6434 utterance +6435 vacuum +6436 vagrancy +6441 valuables +6442 vanquished +6443 vaporizer +6444 varied +6445 vaseline +6446 vegetable +6451 vehicle +6452 velcro +6453 vendor +6454 vertebrae +6455 vestibule +6456 veteran +6461 vexingly +6462 vicinity +6463 videogame +6464 viewfinder +6465 vigilante +6466 village +6511 vinegar +6512 violin +6513 viperfish +6514 virus +6515 visor +6516 vitamins +6521 vivacious +6522 vixen +6523 vocalist +6524 vogue +6525 voicemail +6526 volleyball +6531 voucher +6532 voyage +6533 vulnerable +6534 waffle +6535 wagon +6536 wakeup +6541 walrus +6542 wanderer +6543 wasp +6544 water +6545 waving +6546 wheat +6551 whisper +6552 wholesaler +6553 wick +6554 widow +6555 wielder +6556 wifeless +6561 wikipedia +6562 wildcat +6563 windmill +6564 wipeout +6565 wired +6566 wishbone +6611 wizardry +6612 wobbliness +6613 wolverine +6614 womb +6615 woolworker +6616 workbasket +6621 wound +6622 wrangle +6623 wreckage +6624 wristwatch +6625 wrongdoing +6626 xerox +6631 xylophone +6632 yacht +6633 yahoo +6634 yard +6635 yearbook +6636 yesterday +6641 yiddish +6642 yield +6643 yo-yo +6644 yodel +6645 yogurt +6646 yuppie +6651 zealot +6652 zebra +6653 zeppelin +6654 zestfully +6655 zigzagged +6656 zillion +6661 zipping +6662 zirconium +6663 zodiac +6664 zombie +6665 zookeeper +6666 zucchini From 81293c9c7e3f84af6d9636a923b8406801b34e71 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 15 Nov 2024 13:25:43 -0500 Subject: [PATCH 02/25] initrd/etc/functions: add generate_passphrase logic Nothing uses it for the moment, needs to be called from recovery shell: bash, source /etc/functions. generate_passphrase - parses dictionary to check how many dice rolls needed on first entry, defaults to EFF short list v2 (bigger words easier to remember, 4 dices roll instead of 5) - defaults to using initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt, parametrable - make sure format of dictionary is 'digit word' and fail early otherwise: we expect EFF diceware format dictionaries - enforces max length of 256 chars, parametrable, reduces number of words to fit if not override - enforces default 3 words passphrase, parametrable - enforces captialization of first letter, lowercase parametrable - read multiple bytes from /dev/urandom to fit number of dice rolls Unrelated: uniformize format of file Signed-off-by: Thierry Laurion --- initrd/etc/functions | 162 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 14 deletions(-) diff --git a/initrd/etc/functions b/initrd/etc/functions index 3c4b092c7..6a89dc60a 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -25,7 +25,10 @@ SINK_LOG() { # last (unterminated) line. Add a line break with echo to ensure we # don't lose any input. Buffer up to one blank line so we can avoid # emitting a final (or only) blank line. - (cat; echo) | while IFS= read -r line; do + ( + cat + echo + ) | while IFS= read -r line; do [[ -n "$haveblank" ]] && DEBUG "$name: " # Emit buffered blank line if [[ -z "$line" ]]; then haveblank=y @@ -129,10 +132,10 @@ TRACE_FUNC() { DEBUG_STACK() { local FRAMES FRAMES="${#FUNCNAME[@]}" - DEBUG "call stack: ($((FRAMES-1)) frames)" + DEBUG "call stack: ($((FRAMES - 1)) frames)" # Don't print DEBUG_STACK itself, start from 1 - for i in $(seq 1 "$((FRAMES-1))"); do - DEBUG "- $((i-1)) - ${BASH_SOURCE[$i]}(${BASH_LINENO[$((i-1))]}): ${FUNCNAME[$i]}" + for i in $(seq 1 "$((FRAMES - 1))"); do + DEBUG "- $((i - 1)) - ${BASH_SOURCE[$i]}(${BASH_LINENO[$((i - 1))]}): ${FUNCNAME[$i]}" done } @@ -248,7 +251,7 @@ device_has_partitions() { # In both cases the output is 5 lines: 3 about device info, 1 empty line # and the 5th will be the table header or the invalid message. local DISK_DATA=$(fdisk -l "$DEVICE") - if echo "$DISK_DATA" | grep -q "doesn't contain a valid partition table" || \ + if echo "$DISK_DATA" | grep -q "doesn't contain a valid partition table" || [ "$(echo "$DISK_DATA" | wc -l)" -eq 5 ]; then # No partition table return 1 @@ -305,9 +308,9 @@ list_usb_storage() { done } -# Prompt for a TPM Owner Password if it is not already cached in /tmp/secret/tpm_owner_password. -# Sets tpm_owner_password variable reused in flow, and cache file used until recovery shell is accessed. -# Tools should optionally accept a TPM password on the command line, since some flows need +# Prompt for a TPM Owner Password if it is not already cached in /tmp/secret/tpm_owner_password. +# Sets tpm_owner_password variable reused in flow, and cache file used until recovery shell is accessed. +# Tools should optionally accept a TPM password on the command line, since some flows need # it multiple times and only one prompt is ideal. prompt_tpm_owner_password() { TRACE_FUNC @@ -327,7 +330,7 @@ prompt_tpm_owner_password() { echo -n "$tpm_owner_password" >/tmp/secret/tpm_owner_password || die "Unable to cache TPM owner_password under /tmp/secret/tpm_owner_password" } -# Prompt for a new TPM Owner Password when resetting the TPM. +# Prompt for a new TPM Owner Password when resetting the TPM. # Returned in tpm_owner_passpword and cached under /tpm/secret/tpm_owner_password # The password must be 1-32 characters and must be entered twice, # the script will loop until this is met. @@ -357,7 +360,7 @@ prompt_new_owner_password() { check_tpm_counter() { TRACE_FUNC - + LABEL=${2:-3135106223} tpm_password="$3" # if the /boot.hashes file already exists, read the TPM counter ID @@ -370,7 +373,7 @@ check_tpm_counter() { -pwdc '' \ -la $LABEL | tee /tmp/counter || - die "Unable to create TPM counter" + die "Unable to create TPM counter" TPM_COUNTER=$(cut -d: -f1 [--number_words|-n ] [--max_length|-m ] [--lowercase|-l]" + echo "Generates a passphrase using a Diceware dictionary." + echo " --dictionary|-d Path to the Diceware dictionary file (defaults to /etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt )." + echo " [--number_words|-n ] Number of words in the passphrase (default: 3)." + echo " [--max_length|-m ] Maximum size of the passphrase (default: 256)." + echo " [--lowercase|-l] Use lowercase words (default: false)." + } + + # Helper subfunction to get a word from the dictionary based on dice rolls + get_word_from_dictionary() { + local rolls="$1" + local dictionary_file="$2" + local word="" + + word=$(grep "^$rolls" "$dictionary_file" | awk '{print $2}') + echo "$word" + } + + # Helper subfunction to generate dice rolls + generate_dice_rolls() { + TRACE_FUNC + local num_rolls="$1" + local rolls="" + local random_bytes + + # Read num_rolls bytes from /dev/urandom in one go + random_bytes=$(dd if=/dev/urandom bs=1 count="$num_rolls" 2>/dev/null | hexdump -e '1/1 "%u\n"') + + # Process each byte to generate a dice roll + while read -r byte; do + roll=$((byte % 6 + 1)) + DEBUG "Randomized dice roll: $roll" + rolls+=$roll + done <<<"$random_bytes" + + DEBUG "Generated dice rolls: $rolls" + echo "$rolls" + } + + TRACE_FUNC + local dictionary_file="/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt" + local num_words=3 + local max_size=256 + local lowercase=false + + # Parse parameters + while [[ "$#" -gt 0 ]]; do + case "$1" in + --dictionary | -d) + dictionary_file="$2" + shift + ;; + --lowercase | -l) + lowercase=true + ;; + --number_words | -n) + if ! [[ "$2" =~ ^[0-9]+$ ]] || [[ "$2" -le 0 ]]; then + warn "Invalid number of words: $2" + usage_generate_passphrase + return 1 + fi + num_words="$2" + shift + ;; + --max_length | -m) + if ! [[ "$2" =~ ^[0-9]+$ ]] || [[ "$2" -le 0 ]]; then + warn "Invalid maximum size: $2" + usage_generate_passphrase + return 1 + fi + max_size="$2" + shift + ;; + *) + warn "Unknown parameter: $1" + usage_generate_passphrase + return 1 + ;; + esac + shift + done + + # Validate dictionary file + if [[ -z "$dictionary_file" || ! -f "$dictionary_file" ]]; then + warn "Dictionary file not found or not provided: $dictionary_file" + usage_generate_passphrase + return 1 + fi + + local passphrase="" + local word="" + local key="" + local digits=0 + + # Read the number of digits from the first line of the dictionary file + read -r key _ <"$dictionary_file" + + # Validate that the key is composed entirely of digits + if ! [[ $key =~ ^[0-9]+$ ]]; then + echo "Error: Dictionary is not compliant with EFF diceware dictionaries." + echo "The first line of the dictionary should be in the format: " + echo "Example: 11111 word" + exit 1 + fi + + digits=${#key} + DEBUG "Number of digits in dice rolls: $digits" + + for ((i = 0; i < num_words; ++i)); do + key=$(generate_dice_rolls "$digits") + word=$(get_word_from_dictionary "$key" "$dictionary_file") + DEBUG "Retrieved word: $word" + if [[ "$lowercase" == "false" ]]; then + DEBUG "Capitalizing the first letter of the word" + word=${word^} # Capitalize the first letter + fi + passphrase+="$word " + if [[ ${#passphrase} -gt $max_size ]]; then + DEBUG "Passphrase exceeds max size: $max_size, removing last word" + passphrase=${passphrase% *} # Remove the last word if it exceeds max_size + break + fi + done + + echo "$passphrase" + return 0 +} From 6eac70a31945facb6dd67a0df3cd92b883e57d72 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 17 Nov 2024 14:02:35 -0500 Subject: [PATCH 03/25] WiP initrd/bin/oem-factory-reset: format unification Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 2080 +++++++++++++++++----------------- 1 file changed, 1040 insertions(+), 1040 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 8fa69ca99..533e7252b 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -46,8 +46,8 @@ RSA_KEY_LENGTH=3072 #Override RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards until canokey fixes if [[ "$CONFIG_BOARD_NAME" == qemu-* ]]; then - DEBUG "Overriding RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards" - RSA_KEY_LENGTH=2048 + DEBUG "Overriding RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards" + RSA_KEY_LENGTH=2048 fi GPG_USER_NAME="OEM Key" @@ -60,195 +60,195 @@ SKIP_BOOT="n" die() { - local msg=$1 - if [ -n "$msg" ]; then - echo -e "\n$msg" - fi - kill -s TERM $TOP_PID - exit 1 + local msg=$1 + if [ -n "$msg" ]; then + echo -e "\n$msg" + fi + kill -s TERM $TOP_PID + exit 1 } local_whiptail_error() { - local msg=$1 - if [ "$msg" = "" ]; then - die "whiptail error: An error msg is required" - fi - whiptail_error --msgbox "${msg}\n\n" $HEIGHT $WIDTH --title "Error" + local msg=$1 + if [ "$msg" = "" ]; then + die "whiptail error: An error msg is required" + fi + whiptail_error --msgbox "${msg}\n\n" $HEIGHT $WIDTH --title "Error" } whiptail_error_die() { - local_whiptail_error "$@" - die + local_whiptail_error "$@" + die } mount_boot() { - TRACE_FUNC - # Mount local disk if it is not already mounted. - # Added so that 'o' can be typed early at boot to enter directly into OEM Factory Reset - if ! grep -q /boot /proc/mounts; then - # try to mount if CONFIG_BOOT_DEV exists - if [ -e "$CONFIG_BOOT_DEV" ]; then - mount -o ro $CONFIG_BOOT_DEV /boot || die "Failed to mount $CONFIG_BOOT_DEV. Please change boot device under Configuration > Boot Device" - fi - fi + TRACE_FUNC + # Mount local disk if it is not already mounted. + # Added so that 'o' can be typed early at boot to enter directly into OEM Factory Reset + if ! grep -q /boot /proc/mounts; then + # try to mount if CONFIG_BOOT_DEV exists + if [ -e "$CONFIG_BOOT_DEV" ]; then + mount -o ro $CONFIG_BOOT_DEV /boot || die "Failed to mount $CONFIG_BOOT_DEV. Please change boot device under Configuration > Boot Device" + fi + fi } #Generate a gpg master key: no expiration date, ${RSA_KEY_LENGTH} bits #This key will be used to sign 3 subkeys: encryption, authentication and signing #The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard generate_inmemory_RSA_master_and_subkeys() { - TRACE_FUNC - - echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits master key..." - # Generate GPG master key - { - echo "Key-Type: RSA" # RSA key - echo "Key-Length: ${RSA_KEY_LENGTH}" # RSA key length - echo "Key-Usage: sign" # RSA key usage - echo "Name-Real: ${GPG_USER_NAME}" # User name - echo "Name-Comment: ${GPG_USER_COMMENT}" # User comment - echo "Name-Email: ${GPG_USER_MAIL}" # User email - echo "Expire-Date: 0" # No expiration date - echo "Passphrase: ${ADMIN_PIN}" # Admin PIN - echo "%commit" # Commit changes - } | DO_WITH_DEBUG gpg --expert --batch --command-fd=0 --status-fd=1 --pinentry-mode=loopback --generate-key >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key generation failed!\n\n$ERROR" - fi - - echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits signing subkey..." - # Add signing subkey - { - echo addkey # add key in --edit-key mode - echo 4 # RSA (sign only) - echo ${RSA_KEY_LENGTH} # Signing key size set to RSA_KEY_LENGTH - echo 0 # No expiration date - echo ${ADMIN_PIN} # Local keyring admin pin - echo y # confirm - echo save # save changes and commit to keyring - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key signing subkey generation failed!\n\n$ERROR" - fi - - echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits encryption subkey..." - #Add encryption subkey - { - echo addkey # add key in --edit-key mode - echo 6 # RSA (encrypt only) - echo ${RSA_KEY_LENGTH} # Encryption key size set to RSA_KEY_LENGTH - echo 0 # No expiration date - echo ${ADMIN_PIN} # Local keyring admin pin - echo y # confirm - echo save # save changes and commit to keyring - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key encryption subkey generation failed!\n\n$ERROR" - fi - - echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits authentication subkey..." - #Add authentication subkey - { - #Authentication subkey needs gpg in expert mode to select RSA custom mode (8) - # in order to disable encryption and signing capabilities of subkey - # and then enable authentication capability - echo addkey # add key in --edit-key mode - echo 8 # RSA (set your own capabilities) - echo S # disable sign capability - echo E # disable encryption capability - echo A # enable authentication capability - echo Q # Quit - echo ${RSA_KEY_LENGTH} # Authentication key size set to RSA_KEY_LENGTH - echo 0 # No expiration date - echo ${ADMIN_PIN} # Local keyring admin pin - echo y # confirm - echo save # save changes and commit to keyring - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --expert --edit-key "${GPG_USER_MAIL}" \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key authentication subkey generation failed!\n\n$ERROR" - fi + TRACE_FUNC + + echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits master key..." + # Generate GPG master key + { + echo "Key-Type: RSA" # RSA key + echo "Key-Length: ${RSA_KEY_LENGTH}" # RSA key length + echo "Key-Usage: sign" # RSA key usage + echo "Name-Real: ${GPG_USER_NAME}" # User name + echo "Name-Comment: ${GPG_USER_COMMENT}" # User comment + echo "Name-Email: ${GPG_USER_MAIL}" # User email + echo "Expire-Date: 0" # No expiration date + echo "Passphrase: ${ADMIN_PIN}" # Admin PIN + echo "%commit" # Commit changes + } | DO_WITH_DEBUG gpg --expert --batch --command-fd=0 --status-fd=1 --pinentry-mode=loopback --generate-key >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key generation failed!\n\n$ERROR" + fi + + echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits signing subkey..." + # Add signing subkey + { + echo addkey # add key in --edit-key mode + echo 4 # RSA (sign only) + echo ${RSA_KEY_LENGTH} # Signing key size set to RSA_KEY_LENGTH + echo 0 # No expiration date + echo ${ADMIN_PIN} # Local keyring admin pin + echo y # confirm + echo save # save changes and commit to keyring + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key signing subkey generation failed!\n\n$ERROR" + fi + + echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits encryption subkey..." + #Add encryption subkey + { + echo addkey # add key in --edit-key mode + echo 6 # RSA (encrypt only) + echo ${RSA_KEY_LENGTH} # Encryption key size set to RSA_KEY_LENGTH + echo 0 # No expiration date + echo ${ADMIN_PIN} # Local keyring admin pin + echo y # confirm + echo save # save changes and commit to keyring + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key encryption subkey generation failed!\n\n$ERROR" + fi + + echo "Generating GPG RSA ${RSA_KEY_LENGTH} bits authentication subkey..." + #Add authentication subkey + { + #Authentication subkey needs gpg in expert mode to select RSA custom mode (8) + # in order to disable encryption and signing capabilities of subkey + # and then enable authentication capability + echo addkey # add key in --edit-key mode + echo 8 # RSA (set your own capabilities) + echo S # disable sign capability + echo E # disable encryption capability + echo A # enable authentication capability + echo Q # Quit + echo ${RSA_KEY_LENGTH} # Authentication key size set to RSA_KEY_LENGTH + echo 0 # No expiration date + echo ${ADMIN_PIN} # Local keyring admin pin + echo y # confirm + echo save # save changes and commit to keyring + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --expert --edit-key "${GPG_USER_MAIL}" \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key authentication subkey generation failed!\n\n$ERROR" + fi } #Generate a gpg master key: no expiration date, p256 key (ECC) #This key will be used to sign 3 subkeys: encryption, authentication and signing #The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard generate_inmemory_p256_master_and_subkeys() { - TRACE_FUNC - - echo "Generating GPG p256 bits master key..." - { - echo "Key-Type: ECDSA" # ECDSA key - echo "Key-Curve: nistp256" # ECDSA key curve - echo "Key-Usage: cert" # ECDSA key usage - echo "Name-Real: ${GPG_USER_NAME}" # User name - echo "Name-Comment: ${GPG_USER_COMMENT}" # User comment - echo "Name-Email: ${GPG_USER_MAIL}" # User email - echo "Passphrase: ${ADMIN_PIN}" # Local keyring admin pin - echo "Expire-Date: 0" # No expiration date - echo "%commit" # Commit changes - } | DO_WITH_DEBUG gpg --expert --batch --command-fd=0 --status-fd=1 --pinentry-mode=loopback --generate-key \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG p256 Key generation failed!\n\n$ERROR" - fi - - #Keep Master key fingerprint for add key calls - MASTER_KEY_FP=$(gpg --list-secret-keys --with-colons | grep fpr | cut -d: -f10) - - echo "Generating GPG nistp256 signing subkey..." - { - echo addkey # add key in --edit-key mode - echo 11 # ECC own set capability - echo Q # sign already present, do not modify - echo 3 # P-256 - echo 0 # No validity/expiration date - echo ${ADMIN_PIN} # Local keyring admin pin - echo save # save changes and commit to keyring - } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR_MSG=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "Failed to add ECC nistp256 signing key to master key\n\n${ERROR_MSG}" - fi - - echo "Generating GPG nistp256 encryption subkey..." - { - echo addkey - echo 12 # ECC own set capability - echo Q # Quit - echo 3 # P-256 - echo 0 # No validity/expiration date - echo ${ADMIN_PIN} # Local keyring admin pin - echo save # save changes and commit to keyring - } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR_MSG=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "Failed to add ECC nistp256 encryption key to master key\n\n${ERROR_MSG}" - fi - - echo "Generating GPG nistp256 authentication subkey..." - { - echo addkey # add key in --edit-key mode - echo 11 # ECC own set capability - echo S # deactivate sign - echo A # activate auth - echo Q # Quit - echo 3 # P-256 - echo 0 # no expiration - echo ${ADMIN_PIN} # Local keyring admin pin - echo save # save changes and commit to keyring - } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR_MSG=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "Failed to add ECC nistp256 authentication key to master key\n\n${ERROR_MSG}" - fi + TRACE_FUNC + + echo "Generating GPG p256 bits master key..." + { + echo "Key-Type: ECDSA" # ECDSA key + echo "Key-Curve: nistp256" # ECDSA key curve + echo "Key-Usage: cert" # ECDSA key usage + echo "Name-Real: ${GPG_USER_NAME}" # User name + echo "Name-Comment: ${GPG_USER_COMMENT}" # User comment + echo "Name-Email: ${GPG_USER_MAIL}" # User email + echo "Passphrase: ${ADMIN_PIN}" # Local keyring admin pin + echo "Expire-Date: 0" # No expiration date + echo "%commit" # Commit changes + } | DO_WITH_DEBUG gpg --expert --batch --command-fd=0 --status-fd=1 --pinentry-mode=loopback --generate-key \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG p256 Key generation failed!\n\n$ERROR" + fi + + #Keep Master key fingerprint for add key calls + MASTER_KEY_FP=$(gpg --list-secret-keys --with-colons | grep fpr | cut -d: -f10) + + echo "Generating GPG nistp256 signing subkey..." + { + echo addkey # add key in --edit-key mode + echo 11 # ECC own set capability + echo Q # sign already present, do not modify + echo 3 # P-256 + echo 0 # No validity/expiration date + echo ${ADMIN_PIN} # Local keyring admin pin + echo save # save changes and commit to keyring + } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR_MSG=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "Failed to add ECC nistp256 signing key to master key\n\n${ERROR_MSG}" + fi + + echo "Generating GPG nistp256 encryption subkey..." + { + echo addkey + echo 12 # ECC own set capability + echo Q # Quit + echo 3 # P-256 + echo 0 # No validity/expiration date + echo ${ADMIN_PIN} # Local keyring admin pin + echo save # save changes and commit to keyring + } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR_MSG=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "Failed to add ECC nistp256 encryption key to master key\n\n${ERROR_MSG}" + fi + + echo "Generating GPG nistp256 authentication subkey..." + { + echo addkey # add key in --edit-key mode + echo 11 # ECC own set capability + echo S # deactivate sign + echo A # activate auth + echo Q # Quit + echo 3 # P-256 + echo 0 # no expiration + echo ${ADMIN_PIN} # Local keyring admin pin + echo save # save changes and commit to keyring + } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key ${MASTER_KEY_FP} >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR_MSG=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "Failed to add ECC nistp256 authentication key to master key\n\n${ERROR_MSG}" + fi } @@ -259,141 +259,141 @@ generate_inmemory_p256_master_and_subkeys() { # The master key was already used to sign the subkeys, so it is not needed anymore # Delete the master key from the keyring once key to card is done (already backed up on LUKS private partition) keytocard_subkeys_to_smartcard() { - TRACE_FUNC - - #make sure usb ready and USB Security Dongle ready to communicate with - enable_usb - enable_usb_storage - gpg --card-status >/dev/null 2>&1 || die "Error getting GPG card status" - - gpg_key_factory_reset - - echo "Moving subkeys to smartcard..." - { - echo "key 1" #Toggle on Signature key in --edit-key mode on local keyring - echo "keytocard" #Move Signature key to smartcard - echo "1" #Select Signature key key slot on smartcard - echo "${ADMIN_PIN}" #Local keyring Subkey PIN - echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN - echo "0" #No expiration date - echo "key 1" #Toggle off Signature key - echo "key 2" #Toggle on Encryption key - echo "keytocard" #Move Encryption key to smartcard - echo "2" #Select Encryption key key slot on smartcard - echo "${ADMIN_PIN}" #Local keyring Subkey PIN - echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN - echo "key 2" #Toggle off Encryption key - echo "key 3" #Toggle on Authentication key - echo "keytocard" #Move Authentication key to smartcard - echo "3" #Select Authentication key slot on smartcard - echo "${ADMIN_PIN}" #Local keyring Subkey PIN - echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN - echo "key 3" #Toggle off Authentication key - echo "save" #Save changes and commit to keyring - } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key moving subkeys to smartcard failed!\n\n$ERROR" - fi - - TRACE_FUNC + TRACE_FUNC + + #make sure usb ready and USB Security Dongle ready to communicate with + enable_usb + enable_usb_storage + gpg --card-status >/dev/null 2>&1 || die "Error getting GPG card status" + + gpg_key_factory_reset + + echo "Moving subkeys to smartcard..." + { + echo "key 1" #Toggle on Signature key in --edit-key mode on local keyring + echo "keytocard" #Move Signature key to smartcard + echo "1" #Select Signature key key slot on smartcard + echo "${ADMIN_PIN}" #Local keyring Subkey PIN + echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN + echo "0" #No expiration date + echo "key 1" #Toggle off Signature key + echo "key 2" #Toggle on Encryption key + echo "keytocard" #Move Encryption key to smartcard + echo "2" #Select Encryption key key slot on smartcard + echo "${ADMIN_PIN}" #Local keyring Subkey PIN + echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN + echo "key 2" #Toggle off Encryption key + echo "key 3" #Toggle on Authentication key + echo "keytocard" #Move Authentication key to smartcard + echo "3" #Select Authentication key slot on smartcard + echo "${ADMIN_PIN}" #Local keyring Subkey PIN + echo "${ADMIN_PIN_DEF}" #Smartcard Admin PIN + echo "key 3" #Toggle off Authentication key + echo "save" #Save changes and commit to keyring + } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --edit-key "${GPG_USER_MAIL}" \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key moving subkeys to smartcard failed!\n\n$ERROR" + fi + + TRACE_FUNC } #Whiptail prompt to insert to be wiped thumb drive prompt_insert_to_be_wiped_thumb_drive() { - TRACE_FUNC - #Whiptail warning about having only desired to be wiped thumb drive inserted - whiptail_warning --title 'WARNING: Please insert the thumb drive to be wiped' \ - --msgbox "The thumb drive will be WIPED next.\n\nPlease connect only the thumb drive to be wiped and disconnect others." 0 80 || - die "Error displaying warning about having only desired to be wiped thumb drive inserted" + TRACE_FUNC + #Whiptail warning about having only desired to be wiped thumb drive inserted + whiptail_warning --title 'WARNING: Please insert the thumb drive to be wiped' \ + --msgbox "The thumb drive will be WIPED next.\n\nPlease connect only the thumb drive to be wiped and disconnect others." 0 80 || + die "Error displaying warning about having only desired to be wiped thumb drive inserted" } #export master key and subkeys to thumbdrive's private LUKS contained partition export_master_key_subkeys_and_revocation_key_to_private_LUKS_container() { - TRACE_FUNC - - #Sanity check on passed arguments - while [ $# -gt 0 ]; do - case "$1" in - --mode) - mode="$2" - shift - shift - ;; - --device) - device="$2" - shift - shift - ;; - --mountpoint) - mountpoint="$2" - shift - shift - ;; - --pass) - pass="${2}" - shift - shift - ;; - *) - die "Error: unknown argument: $1" - ;; - esac - done - - mount-usb --mode "$mode" --device "$device" --mountpoint "$mountpoint" --pass "$pass" || die "Error mounting thumb drive's private partition" - - #Export master key and subkeys to thumb drive - DEBUG "Exporting master key and subkeys to private LUKS container's partition..." - - gpg --export-secret-key --armor --pinentry-mode loopback --passphrase="${pass}" "${GPG_USER_MAIL}" >"$mountpoint"/privkey.sec || - die "Error exporting master key to private LUKS container's partition" - gpg --export-secret-subkeys --armor --pinentry-mode loopback --passphrase="${pass}" "${GPG_USER_MAIL}" >"$mountpoint"/subkeys.sec || - die "Error exporting subkeys to private LUKS container's partition" - #copy whole keyring to thumb drive, including revocation key and trust database - cp -af ~/.gnupg "$mountpoint"/.gnupg || die "Error copying whole keyring to private LUKS container's partition" - #Unmount private LUKS container's mount point - umount "$mountpoint" || die "Error unmounting private LUKS container's mount point" - - TRACE_FUNC + TRACE_FUNC + + #Sanity check on passed arguments + while [ $# -gt 0 ]; do + case "$1" in + --mode) + mode="$2" + shift + shift + ;; + --device) + device="$2" + shift + shift + ;; + --mountpoint) + mountpoint="$2" + shift + shift + ;; + --pass) + pass="${2}" + shift + shift + ;; + *) + die "Error: unknown argument: $1" + ;; + esac + done + + mount-usb --mode "$mode" --device "$device" --mountpoint "$mountpoint" --pass "$pass" || die "Error mounting thumb drive's private partition" + + #Export master key and subkeys to thumb drive + DEBUG "Exporting master key and subkeys to private LUKS container's partition..." + + gpg --export-secret-key --armor --pinentry-mode loopback --passphrase="${pass}" "${GPG_USER_MAIL}" >"$mountpoint"/privkey.sec || + die "Error exporting master key to private LUKS container's partition" + gpg --export-secret-subkeys --armor --pinentry-mode loopback --passphrase="${pass}" "${GPG_USER_MAIL}" >"$mountpoint"/subkeys.sec || + die "Error exporting subkeys to private LUKS container's partition" + #copy whole keyring to thumb drive, including revocation key and trust database + cp -af ~/.gnupg "$mountpoint"/.gnupg || die "Error copying whole keyring to private LUKS container's partition" + #Unmount private LUKS container's mount point + umount "$mountpoint" || die "Error unmounting private LUKS container's mount point" + + TRACE_FUNC } #Export public key to thumb drive's public partition export_public_key_to_thumbdrive_public_partition() { - TRACE_FUNC - - #Sanity check on passed arguments - while [ $# -gt 0 ]; do - case "$1" in - --mode) - mode="$2" - shift - shift - ;; - --device) - device="$2" - shift - shift - ;; - --mountpoint) - mountpoint="$2" - shift - shift - ;; - *) - die "Error: unknown argument: $1" - ;; - esac - done - - #pass non-empty arguments to --pass, --mountpoint, --device, --mode - mount-usb --device "$device" --mode "$mode" --mountpoint "$mountpoint" || die "Error mounting thumb drive's public partition" - #TODO: reuse "Obtain GPG key ID" so that pubkey on public thumb drive partition is named after key ID - gpg --export --armor "${GPG_USER_MAIL}" >"$mountpoint"/pubkey.asc || die "Error exporting public key to thumb drive's public partition" - umount "$mountpoint" || die "Error unmounting thumb drive's public partition" - - TRACE_FUNC + TRACE_FUNC + + #Sanity check on passed arguments + while [ $# -gt 0 ]; do + case "$1" in + --mode) + mode="$2" + shift + shift + ;; + --device) + device="$2" + shift + shift + ;; + --mountpoint) + mountpoint="$2" + shift + shift + ;; + *) + die "Error: unknown argument: $1" + ;; + esac + done + + #pass non-empty arguments to --pass, --mountpoint, --device, --mode + mount-usb --device "$device" --mode "$mode" --mountpoint "$mountpoint" || die "Error mounting thumb drive's public partition" + #TODO: reuse "Obtain GPG key ID" so that pubkey on public thumb drive partition is named after key ID + gpg --export --armor "${GPG_USER_MAIL}" >"$mountpoint"/pubkey.asc || die "Error exporting public key to thumb drive's public partition" + umount "$mountpoint" || die "Error unmounting thumb drive's public partition" + + TRACE_FUNC } # Select thumb drive and LUKS container size for GPG key export @@ -401,467 +401,467 @@ export_public_key_to_thumbdrive_public_partition() { # - thumb_drive # - thumb_drive_luks_percent select_thumb_drive_for_key_material() { - TRACE_FUNC - - #enable usb storage - enable_usb - enable_usb_storage - - prompt_insert_to_be_wiped_thumb_drive - - #loop until user chooses a disk - thumb_drive="" - while [ -z "$thumb_drive" ]; do - #list usb storage devices - list_usb_storage disks >/tmp/usb_disk_list - # Abort if: - # - no disks found (prevent file_selector's nonsense prompt) - # - file_selector fails for any reason - # - user aborts (file_selector succeeds but FILE is empty) - if [ $(cat /tmp/usb_disk_list | wc -l) -gt 0 ] && - file_selector --show-size "/tmp/usb_disk_list" "Select USB device to partition" && - [ -n "$FILE" ]; then - # Obtain size of thumb drive to be wiped with fdisk - disk_size_bytes="$(blockdev --getsize64 "$FILE")" - if [ "$disk_size_bytes" -lt "$((128*1024*1024))" ]; then - warn "Thumb drive size is less than 128MB!" - warn "LUKS container needs to be at least 8MB!" - warn "If the next operation fails, try with a bigger thumb drive" - fi - - select_luks_container_size_percent - thumb_drive_luks_percent="$(cat /tmp/luks_container_size_percent)" - - if ! confirm_thumb_drive_format "$FILE" "$thumb_drive_luks_percent"; then - warn "Thumb drive wipe aborted by user!" - continue - fi - - #User chose and confirmed a thumb drive and its size to be wiped - thumb_drive=$FILE - else - #No USB storage device detected - warn "No USB storage device detected! Aborting OEM Factory Reset / Re-Ownership" - sleep 3 - die "No USB storage device detected! User decided to not wipe any thumb drive" - fi - done - thumb_drive_luks_percent="$(cat /tmp/luks_container_size_percent)" + TRACE_FUNC + + #enable usb storage + enable_usb + enable_usb_storage + + prompt_insert_to_be_wiped_thumb_drive + + #loop until user chooses a disk + thumb_drive="" + while [ -z "$thumb_drive" ]; do + #list usb storage devices + list_usb_storage disks >/tmp/usb_disk_list + # Abort if: + # - no disks found (prevent file_selector's nonsense prompt) + # - file_selector fails for any reason + # - user aborts (file_selector succeeds but FILE is empty) + if [ $(cat /tmp/usb_disk_list | wc -l) -gt 0 ] && + file_selector --show-size "/tmp/usb_disk_list" "Select USB device to partition" && + [ -n "$FILE" ]; then + # Obtain size of thumb drive to be wiped with fdisk + disk_size_bytes="$(blockdev --getsize64 "$FILE")" + if [ "$disk_size_bytes" -lt "$((128 * 1024 * 1024))" ]; then + warn "Thumb drive size is less than 128MB!" + warn "LUKS container needs to be at least 8MB!" + warn "If the next operation fails, try with a bigger thumb drive" + fi + + select_luks_container_size_percent + thumb_drive_luks_percent="$(cat /tmp/luks_container_size_percent)" + + if ! confirm_thumb_drive_format "$FILE" "$thumb_drive_luks_percent"; then + warn "Thumb drive wipe aborted by user!" + continue + fi + + #User chose and confirmed a thumb drive and its size to be wiped + thumb_drive=$FILE + else + #No USB storage device detected + warn "No USB storage device detected! Aborting OEM Factory Reset / Re-Ownership" + sleep 3 + die "No USB storage device detected! User decided to not wipe any thumb drive" + fi + done + thumb_drive_luks_percent="$(cat /tmp/luks_container_size_percent)" } #Wipe a thumb drive and export master key and subkeys to it # $1 - thumb drive block device # $2 - LUKS container percentage [1-99] wipe_thumb_drive_and_copy_gpg_key_material() { - TRACE_FUNC + TRACE_FUNC - local thumb_drive thumb_drive_luks_percent - thumb_drive="$1" - thumb_drive_luks_percent="$2" + local thumb_drive thumb_drive_luks_percent + thumb_drive="$1" + thumb_drive_luks_percent="$2" - #Wipe thumb drive with a LUKS container of size $(cat /tmp/luks_container_size_percent) - prepare_thumb_drive "$thumb_drive" "$thumb_drive_luks_percent" "${ADMIN_PIN}" - #Export master key and subkeys to thumb drive first partition - export_master_key_subkeys_and_revocation_key_to_private_LUKS_container --mode rw --device "$thumb_drive"1 --mountpoint /media --pass "${ADMIN_PIN}" - #Export public key to thumb drive's public partition - export_public_key_to_thumbdrive_public_partition --mode rw --device "$thumb_drive"2 --mountpoint /media + #Wipe thumb drive with a LUKS container of size $(cat /tmp/luks_container_size_percent) + prepare_thumb_drive "$thumb_drive" "$thumb_drive_luks_percent" "${ADMIN_PIN}" + #Export master key and subkeys to thumb drive first partition + export_master_key_subkeys_and_revocation_key_to_private_LUKS_container --mode rw --device "$thumb_drive"1 --mountpoint /media --pass "${ADMIN_PIN}" + #Export public key to thumb drive's public partition + export_public_key_to_thumbdrive_public_partition --mode rw --device "$thumb_drive"2 --mountpoint /media - TRACE_FUNC + TRACE_FUNC } gpg_key_factory_reset() { - TRACE_FUNC - - #enable usb storage - enable_usb - - # Factory reset GPG card - echo "GPG factory reset of USB Security Dongle's smartcard..." - { - echo admin # admin menu - echo factory-reset # factory reset smartcard - echo y # confirm - echo yes # confirm - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR" - fi - # If Nitrokey Storage is inserted, reset AES keys as well - if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then - DEBUG "Nitrokey Storage detected, resetting AES keys..." - /bin/hotp_verification regenerate ${ADMIN_PIN_DEF} - DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" - killall -9 scdaemon - fi - # Toggle forced sig (good security practice, forcing PIN request for each signature request) - if gpg --card-status | grep "Signature PIN" | grep -q "not forced"; then - DEBUG "GPG toggling forcesig on since off..." - { - echo admin # admin menu - echo forcesig # toggle forcesig - echo ${ADMIN_PIN_DEF} # local keyring PIN - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key forcesig toggle on failed!\n\n$ERROR" - fi - fi - # use p256 for key generation if requested - if [ "$GPG_ALGO" = "p256" ]; then - { - echo admin # admin menu - echo key-attr # key attributes - echo 2 # ECC - echo 3 # P-256 - echo ${ADMIN_PIN_DEF} # local keyring PIN - echo 2 # ECC - echo 3 # P-256 - echo ${ADMIN_PIN_DEF} # local keyring PIN - echo 2 # ECC - echo 3 # P-256 - echo ${ADMIN_PIN_DEF} # local keyring PIN - } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "Setting key to NIST-P256 in USB Security Dongle failed." - fi - # fallback to RSA key generation by default - elif [ "$GPG_ALGO" = "RSA" ]; then - DEBUG "GPG setting RSA key length to ${RSA_KEY_LENGTH} bits..." - # Set RSA key length - { - echo admin - echo key-attr - echo 1 # RSA - echo ${RSA_KEY_LENGTH} #Signing key size set to RSA_KEY_LENGTH - echo ${ADMIN_PIN_DEF} #Local keyring PIN - echo 1 # RSA - echo ${RSA_KEY_LENGTH} #Encryption key size set to RSA_KEY_LENGTH - echo ${ADMIN_PIN_DEF} #Local keyring PIN - echo 1 # RSA - echo ${RSA_KEY_LENGTH} #Authentication key size set to RSA_KEY_LENGTH - echo ${ADMIN_PIN_DEF} #Local keyring PIN - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "Setting key attributed to RSA ${RSA_KEY_LENGTH} bits in USB Security Dongle failed." - fi - else - #Unknown GPG_ALGO - whiptail_error_die "Unknown GPG_ALGO: $GPG_ALGO" - fi - - TRACE_FUNC + TRACE_FUNC + + #enable usb storage + enable_usb + + # Factory reset GPG card + echo "GPG factory reset of USB Security Dongle's smartcard..." + { + echo admin # admin menu + echo factory-reset # factory reset smartcard + echo y # confirm + echo yes # confirm + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR" + fi + # If Nitrokey Storage is inserted, reset AES keys as well + if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then + DEBUG "Nitrokey Storage detected, resetting AES keys..." + /bin/hotp_verification regenerate ${ADMIN_PIN_DEF} + DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" + killall -9 scdaemon + fi + # Toggle forced sig (good security practice, forcing PIN request for each signature request) + if gpg --card-status | grep "Signature PIN" | grep -q "not forced"; then + DEBUG "GPG toggling forcesig on since off..." + { + echo admin # admin menu + echo forcesig # toggle forcesig + echo ${ADMIN_PIN_DEF} # local keyring PIN + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key forcesig toggle on failed!\n\n$ERROR" + fi + fi + # use p256 for key generation if requested + if [ "$GPG_ALGO" = "p256" ]; then + { + echo admin # admin menu + echo key-attr # key attributes + echo 2 # ECC + echo 3 # P-256 + echo ${ADMIN_PIN_DEF} # local keyring PIN + echo 2 # ECC + echo 3 # P-256 + echo ${ADMIN_PIN_DEF} # local keyring PIN + echo 2 # ECC + echo 3 # P-256 + echo ${ADMIN_PIN_DEF} # local keyring PIN + } | DO_WITH_DEBUG gpg --expert --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "Setting key to NIST-P256 in USB Security Dongle failed." + fi + # fallback to RSA key generation by default + elif [ "$GPG_ALGO" = "RSA" ]; then + DEBUG "GPG setting RSA key length to ${RSA_KEY_LENGTH} bits..." + # Set RSA key length + { + echo admin + echo key-attr + echo 1 # RSA + echo ${RSA_KEY_LENGTH} #Signing key size set to RSA_KEY_LENGTH + echo ${ADMIN_PIN_DEF} #Local keyring PIN + echo 1 # RSA + echo ${RSA_KEY_LENGTH} #Encryption key size set to RSA_KEY_LENGTH + echo ${ADMIN_PIN_DEF} #Local keyring PIN + echo 1 # RSA + echo ${RSA_KEY_LENGTH} #Authentication key size set to RSA_KEY_LENGTH + echo ${ADMIN_PIN_DEF} #Local keyring PIN + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "Setting key attributed to RSA ${RSA_KEY_LENGTH} bits in USB Security Dongle failed." + fi + else + #Unknown GPG_ALGO + whiptail_error_die "Unknown GPG_ALGO: $GPG_ALGO" + fi + + TRACE_FUNC } generate_OEM_gpg_keys() { - TRACE_FUNC - - #This function simply generates subkeys in smartcard following smarcard config from gpg_key_factory_reset - echo "Generating GPG keys in USB Security Dongle's smartcard..." - { - echo admin # admin menu - echo generate # generate keys - echo n # Do not export keys - echo ${ADMIN_PIN_DEF} # Default admin PIN since we just factory reset - echo ${USER_PIN_DEF} # Default user PIN since we just factory reset - echo 0 # No key expiration - echo ${GPG_USER_NAME} # User name - echo ${GPG_USER_MAIL} # User email - echo ${GPG_USER_COMMENT} # User comment - echo ${USER_PIN_DEF} # Default user PIN since we just factory reset - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output) - whiptail_error_die "GPG Key automatic keygen failed!\n\n$ERROR" - fi - - TRACE_FUNC + TRACE_FUNC + + #This function simply generates subkeys in smartcard following smarcard config from gpg_key_factory_reset + echo "Generating GPG keys in USB Security Dongle's smartcard..." + { + echo admin # admin menu + echo generate # generate keys + echo n # Do not export keys + echo ${ADMIN_PIN_DEF} # Default admin PIN since we just factory reset + echo ${USER_PIN_DEF} # Default user PIN since we just factory reset + echo 0 # No key expiration + echo ${GPG_USER_NAME} # User name + echo ${GPG_USER_MAIL} # User email + echo ${GPG_USER_COMMENT} # User comment + echo ${USER_PIN_DEF} # Default user PIN since we just factory reset + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output) + whiptail_error_die "GPG Key automatic keygen failed!\n\n$ERROR" + fi + + TRACE_FUNC } gpg_key_change_pin() { - TRACE_FUNC - DEBUG "Changing GPG key PIN" - # 1 = user PIN, 3 = admin PIN - PIN_TYPE=$1 - PIN_ORIG=${2} - PIN_NEW=${3} - # Change PIN - { - echo admin # admin menu - echo passwd # change PIN - echo ${PIN_TYPE} # 1 = user PIN, 3 = admin PIN - echo ${PIN_ORIG} # old PIN - echo ${PIN_NEW} # new PIN - echo ${PIN_NEW} # confirm new PIN - echo q # quit - echo q - } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \ - >/tmp/gpg_card_edit_output 2>&1 - if [ $? -ne 0 ]; then - ERROR=$(cat /tmp/gpg_card_edit_output | fold -s) - whiptail_error_die "GPG Key PIN change failed!\n\n$ERROR" - fi - - TRACE_FUNC + TRACE_FUNC + DEBUG "Changing GPG key PIN" + # 1 = user PIN, 3 = admin PIN + PIN_TYPE=$1 + PIN_ORIG=${2} + PIN_NEW=${3} + # Change PIN + { + echo admin # admin menu + echo passwd # change PIN + echo ${PIN_TYPE} # 1 = user PIN, 3 = admin PIN + echo ${PIN_ORIG} # old PIN + echo ${PIN_NEW} # new PIN + echo ${PIN_NEW} # confirm new PIN + echo q # quit + echo q + } | DO_WITH_DEBUG gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \ + >/tmp/gpg_card_edit_output 2>&1 + if [ $? -ne 0 ]; then + ERROR=$(cat /tmp/gpg_card_edit_output | fold -s) + whiptail_error_die "GPG Key PIN change failed!\n\n$ERROR" + fi + + TRACE_FUNC } generate_checksums() { - TRACE_FUNC - - # ensure /boot mounted - if ! grep -q /boot /proc/mounts; then - mount -o rw /boot || whiptail_error_die "Unable to mount /boot" - else - mount -o remount,rw /boot || whiptail_error_die "Unable to mount /boot" - fi - - #Check if previous LUKS TPM Disk Unlock Key was set - if [ -e /boot/kexec_key_devices.txt ]; then - TPM_DISK_ENCRYPTION_KEY_SET=1 - fi - - # clear any existing checksums/signatures - rm /boot/kexec* 2>/dev/null - - # create Heads TPM counter - if [ "$CONFIG_TPM" = "y" ]; then - if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then - tpmr counter_create \ - -pwdc '' \ - -la -3135106223 | - tee /tmp/counter || - whiptail_error_die "Unable to create TPM counter" - TPM_COUNTER=$(cut -d: -f1 /dev/null 2>&1 || - whiptail_error_die "Unable to increment tpm counter" - - # create rollback file - sha256sum /tmp/counter-$TPM_COUNTER >/boot/kexec_rollback.txt 2>/dev/null || - whiptail_error_die "Unable to create rollback file" - else - ## needs to exist for initial call to unseal-hotp - echo "0" >/boot/kexec_hotp_counter - fi - fi - - # set default boot option only if no LUKS TPM Disk Unlock Key previously set - if [ -z "$TPM_DISK_ENCRYPTION_KEY_SET" ]; then - set_default_boot_option - fi - - DEBUG "Generating hashes" - ( - set -e -o pipefail - cd /boot - find ./ -type f ! -path './kexec*' -print0 | - xargs -0 sha256sum >/boot/kexec_hashes.txt 2>/dev/null - print_tree >/boot/kexec_tree.txt - ) - [ $? -eq 0 ] || whiptail_error_die "Error generating kexec hashes" - - param_files=$(find /boot/kexec*.txt) - [ -z "$param_files" ] && - whiptail_error_die "No kexec parameter files to sign" - - if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" -a "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "n" ]; then - #The local keyring used to generate in memory subkeys is still valid since no key has been moved to smartcard - #Local keyring passwd is ADMIN_PIN. We need to set USER_PIN to ADMIN_PIN to be able to sign next in this boot session - DEBUG "Setting GPG User PIN to GPG Admin PIN so local keyring can be used to detach-sign kexec files next" - USER_PIN=$ADMIN_PIN - fi - - DEBUG "Detach-signing boot files under kexec.sig: ${param_files}" - if sha256sum $param_files 2>/dev/null | DO_WITH_DEBUG --mask-position 4 gpg \ - --pinentry-mode loopback \ - --passphrase "${USER_PIN}" \ - --digest-algo SHA256 \ - --detach-sign \ - -a \ - >/boot/kexec.sig 2>/tmp/error; then - # successful - update the validated params - if ! check_config /boot >/dev/null 2>/tmp/error; then - cat /tmp/error - ret=1 - else - ret=0 - fi - else - cat /tmp/error - ret=1 - fi - - # done writing to /boot, switch back to RO - mount -o ro,remount /boot - - if [ $ret = 1 ]; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error signing kexec boot files:\n\n$ERROR" - fi - - TRACE_FUNC + TRACE_FUNC + + # ensure /boot mounted + if ! grep -q /boot /proc/mounts; then + mount -o rw /boot || whiptail_error_die "Unable to mount /boot" + else + mount -o remount,rw /boot || whiptail_error_die "Unable to mount /boot" + fi + + #Check if previous LUKS TPM Disk Unlock Key was set + if [ -e /boot/kexec_key_devices.txt ]; then + TPM_DISK_ENCRYPTION_KEY_SET=1 + fi + + # clear any existing checksums/signatures + rm /boot/kexec* 2>/dev/null + + # create Heads TPM counter + if [ "$CONFIG_TPM" = "y" ]; then + if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + tpmr counter_create \ + -pwdc '' \ + -la -3135106223 | + tee /tmp/counter || + whiptail_error_die "Unable to create TPM counter" + TPM_COUNTER=$(cut -d: -f1 /dev/null 2>&1 || + whiptail_error_die "Unable to increment tpm counter" + + # create rollback file + sha256sum /tmp/counter-$TPM_COUNTER >/boot/kexec_rollback.txt 2>/dev/null || + whiptail_error_die "Unable to create rollback file" + else + ## needs to exist for initial call to unseal-hotp + echo "0" >/boot/kexec_hotp_counter + fi + fi + + # set default boot option only if no LUKS TPM Disk Unlock Key previously set + if [ -z "$TPM_DISK_ENCRYPTION_KEY_SET" ]; then + set_default_boot_option + fi + + DEBUG "Generating hashes" + ( + set -e -o pipefail + cd /boot + find ./ -type f ! -path './kexec*' -print0 | + xargs -0 sha256sum >/boot/kexec_hashes.txt 2>/dev/null + print_tree >/boot/kexec_tree.txt + ) + [ $? -eq 0 ] || whiptail_error_die "Error generating kexec hashes" + + param_files=$(find /boot/kexec*.txt) + [ -z "$param_files" ] && + whiptail_error_die "No kexec parameter files to sign" + + if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" -a "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "n" ]; then + #The local keyring used to generate in memory subkeys is still valid since no key has been moved to smartcard + #Local keyring passwd is ADMIN_PIN. We need to set USER_PIN to ADMIN_PIN to be able to sign next in this boot session + DEBUG "Setting GPG User PIN to GPG Admin PIN so local keyring can be used to detach-sign kexec files next" + USER_PIN=$ADMIN_PIN + fi + + DEBUG "Detach-signing boot files under kexec.sig: ${param_files}" + if sha256sum $param_files 2>/dev/null | DO_WITH_DEBUG gpg \ + --pinentry-mode loopback \ + --passphrase "${USER_PIN}" \ + --digest-algo SHA256 \ + --detach-sign \ + -a \ + >/boot/kexec.sig 2>/tmp/error; then + # successful - update the validated params + if ! check_config /boot >/dev/null 2>/tmp/error; then + cat /tmp/error + ret=1 + else + ret=0 + fi + else + cat /tmp/error + ret=1 + fi + + # done writing to /boot, switch back to RO + mount -o ro,remount /boot + + if [ $ret = 1 ]; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error signing kexec boot files:\n\n$ERROR" + fi + + TRACE_FUNC } set_default_boot_option() { - TRACE_FUNC - - option_file="/tmp/kexec_options.txt" - tmp_menu_file="/tmp/kexec/kexec_menu.txt" - hash_file="/boot/kexec_default_hashes.txt" - - mkdir -p /tmp/kexec/ - rm $option_file 2>/dev/null - # parse boot options from grub.cfg - for i in $(find /boot -name "grub.cfg"); do - kexec-parse-boot "/boot" "$i" >>$option_file - done - # FC29/30+ may use BLS format grub config files - # https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault - # only parse these if $option_file is still empty - if [ ! -s $option_file ] && [ -d "/boot/loader/entries" ]; then - for i in $(find /boot -name "grub.cfg"); do - kexec-parse-bls "/boot" "$i" "/boot/loader/entries" >>$option_file - done - fi - [ ! -s $option_file ] && - whiptail_error_die "Failed to parse any boot options" - - # sort boot options - sort -r $option_file | uniq >$tmp_menu_file - - ## save first option as default - entry=$(head -n 1 $tmp_menu_file | tail -1) - - # clear existing default configs - rm "/boot/kexec_default.*.txt" 2>/dev/null - - # get correct index for entry - index=$(grep -n "$entry" $option_file | cut -f1 -d ':') - - # write new config - echo "$entry" >/boot/kexec_default.$index.txt - - # validate boot option - (cd /boot && /bin/kexec-boot -b "/boot" -e "$entry" -f | - xargs sha256sum >$hash_file 2>/dev/null) || - whiptail_error_die "Failed to create hashes of boot files" - - TRACE_FUNC + TRACE_FUNC + + option_file="/tmp/kexec_options.txt" + tmp_menu_file="/tmp/kexec/kexec_menu.txt" + hash_file="/boot/kexec_default_hashes.txt" + + mkdir -p /tmp/kexec/ + rm $option_file 2>/dev/null + # parse boot options from grub.cfg + for i in $(find /boot -name "grub.cfg"); do + kexec-parse-boot "/boot" "$i" >>$option_file + done + # FC29/30+ may use BLS format grub config files + # https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault + # only parse these if $option_file is still empty + if [ ! -s $option_file ] && [ -d "/boot/loader/entries" ]; then + for i in $(find /boot -name "grub.cfg"); do + kexec-parse-bls "/boot" "$i" "/boot/loader/entries" >>$option_file + done + fi + [ ! -s $option_file ] && + whiptail_error_die "Failed to parse any boot options" + + # sort boot options + sort -r $option_file | uniq >$tmp_menu_file + + ## save first option as default + entry=$(head -n 1 $tmp_menu_file | tail -1) + + # clear existing default configs + rm "/boot/kexec_default.*.txt" 2>/dev/null + + # get correct index for entry + index=$(grep -n "$entry" $option_file | cut -f1 -d ':') + + # write new config + echo "$entry" >/boot/kexec_default.$index.txt + + # validate boot option + (cd /boot && /bin/kexec-boot -b "/boot" -e "$entry" -f | + xargs sha256sum >$hash_file 2>/dev/null) || + whiptail_error_die "Failed to create hashes of boot files" + + TRACE_FUNC } report_integrity_measurements() { - TRACE_FUNC - - #check for GPG key in keyring - GPG_KEY_COUNT=$(gpg -k 2>/dev/null | wc -l) - if [ "$GPG_KEY_COUNT" -ne 0 ]; then - # Check and report TOTP - # update the TOTP code every thirty seconds - date=$(date "+%Y-%m-%d %H:%M:%S %Z") - seconds=$(date "+%s") - half=$(expr \( "$seconds" % 60 \) / 30) - if [ "$CONFIG_TPM" != "y" ]; then - TOTP="NO TPM" - elif [ "$half" != "$last_half" ]; then - last_half=$half - TOTP=$(unseal-totp) >/dev/null 2>&1 - fi - - # Check and report on HOTP status - if [ -x /bin/hotp_verification ]; then - HOTP="Unverified" - enable_usb - for attempt in 1 2 3; do - if ! hotp_verification info >/dev/null 2>&1; then - whiptail_warning --title "WARNING: Please insert your HOTP enabled USB Security Dongle (Attempt $attempt/3)" --msgbox "Your HOTP enabled USB Security Dongle was not detected.\n\nPlease remove it and insert it again." 0 80 - else - break - fi - done - - if [ $attempt -eq 3 ]; then - die "No HOTP enabled USB Security Dongle detected. Please disable 'CONFIG_HOTPKEY' in the board config and rebuild." - fi - - # Don't output HOTP codes to screen, so as to make replay attacks harder - HOTP=$(unseal-hotp) >/dev/null 2>&1 - hotp_verification check $HOTP - case "$?" in - 0) - HOTP="Success" - ;; - 4) - HOTP="Invalid code" - BG_COLOR_MAIN_MENU="error" - ;; - *) - HOTP="Error checking code, Insert USB Security Dongle and retry" - BG_COLOR_MAIN_MENU="warning" - ;; - esac - else - HOTP='N/A' - fi - # Check for detached signed digest and report on /boot integrity status - check_config /boot force - TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt" - - if (cd /boot && sha256sum -c "$TMP_HASH_FILE" >/tmp/hash_output); then - HASH="OK" - else - HASH="ALTERED" - fi - - #Show results - whiptail_type $BG_COLOR_MAIN_MENU --title "Measured Integrity Report" --msgbox "$date\nTOTP: $TOTP | HOTP: $HOTP\n/BOOT INTEGRITY: $HASH\n\nPress OK to continue or Ctrl+Alt+Delete to reboot" 0 80 - fi - - TRACE_FUNC + TRACE_FUNC + + #check for GPG key in keyring + GPG_KEY_COUNT=$(gpg -k 2>/dev/null | wc -l) + if [ "$GPG_KEY_COUNT" -ne 0 ]; then + # Check and report TOTP + # update the TOTP code every thirty seconds + date=$(date "+%Y-%m-%d %H:%M:%S %Z") + seconds=$(date "+%s") + half=$(expr \( "$seconds" % 60 \) / 30) + if [ "$CONFIG_TPM" != "y" ]; then + TOTP="NO TPM" + elif [ "$half" != "$last_half" ]; then + last_half=$half + TOTP=$(unseal-totp) >/dev/null 2>&1 + fi + + # Check and report on HOTP status + if [ -x /bin/hotp_verification ]; then + HOTP="Unverified" + enable_usb + for attempt in 1 2 3; do + if ! hotp_verification info >/dev/null 2>&1; then + whiptail_warning --title "WARNING: Please insert your HOTP enabled USB Security Dongle (Attempt $attempt/3)" --msgbox "Your HOTP enabled USB Security Dongle was not detected.\n\nPlease remove it and insert it again." 0 80 + else + break + fi + done + + if [ $attempt -eq 3 ]; then + die "No HOTP enabled USB Security Dongle detected. Please disable 'CONFIG_HOTPKEY' in the board config and rebuild." + fi + + # Don't output HOTP codes to screen, so as to make replay attacks harder + HOTP=$(unseal-hotp) >/dev/null 2>&1 + hotp_verification check $HOTP + case "$?" in + 0) + HOTP="Success" + ;; + 4) + HOTP="Invalid code" + BG_COLOR_MAIN_MENU="error" + ;; + *) + HOTP="Error checking code, Insert USB Security Dongle and retry" + BG_COLOR_MAIN_MENU="warning" + ;; + esac + else + HOTP='N/A' + fi + # Check for detached signed digest and report on /boot integrity status + check_config /boot force + TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt" + + if (cd /boot && sha256sum -c "$TMP_HASH_FILE" >/tmp/hash_output); then + HASH="OK" + else + HASH="ALTERED" + fi + + #Show results + whiptail_type $BG_COLOR_MAIN_MENU --title "Measured Integrity Report" --msgbox "$date\nTOTP: $TOTP | HOTP: $HOTP\n/BOOT INTEGRITY: $HASH\n\nPress OK to continue or Ctrl+Alt+Delete to reboot" 0 80 + fi + + TRACE_FUNC } usb_security_token_capabilities_check() { - TRACE_FUNC - - enable_usb - # ... first set board config preference - if [ -n "$CONFIG_GPG_ALGO" ]; then - GPG_ALGO=$CONFIG_GPG_ALGO - DEBUG "Setting GPG_ALGO to (board-)configured: $CONFIG_GPG_ALGO" - fi - # ... overwrite with usb-token capability - if lsusb | grep -q "20a0:42b2"; then - GPG_ALGO="p256" - DEBUG "Nitrokey 3 detected: Setting GPG_ALGO to: $GPG_ALGO" - fi + TRACE_FUNC + + enable_usb + # ... first set board config preference + if [ -n "$CONFIG_GPG_ALGO" ]; then + GPG_ALGO=$CONFIG_GPG_ALGO + DEBUG "Setting GPG_ALGO to (board-)configured: $CONFIG_GPG_ALGO" + fi + # ... overwrite with usb-token capability + if lsusb | grep -q "20a0:42b2"; then + GPG_ALGO="p256" + DEBUG "Nitrokey 3 detected: Setting GPG_ALGO to: $GPG_ALGO" + fi } ## main script start # check for args if [ "$1" != "" ]; then - title_text=$1 + title_text=$1 else - title_text="OEM Factory Reset / Re-Ownership" + title_text="OEM Factory Reset / Re-Ownership" fi if [ "$2" != "" ]; then - bg_color=$2 + bg_color=$2 else - bg_color="" + bg_color="" fi # show warning prompt if [ "$CONFIG_TPM" = "y" ]; then - TPM_STR=" * ERASE the TPM and own it with a password\n" + TPM_STR=" * ERASE the TPM and own it with a password\n" else - TPM_STR="" + TPM_STR="" fi if ! whiptail_warning --yesno " This operation will automatically:\n @@ -873,8 +873,8 @@ $TPM_STR * Sign all of the files in /boot with the new GPG key\n\n It requires that you already have an OS installed on a\n dedicated /boot partition. Do you wish to continue?" \ - $HEIGHT $WIDTH $CONTINUE $CANCEL $CLEAR --title "$title_text"; then - exit 1 + $HEIGHT $WIDTH $CONTINUE $CANCEL $CLEAR --title "$title_text"; then + exit 1 fi #Make sure /boot is mounted if board config defines default @@ -891,191 +891,191 @@ echo -e -n "Would you like to use default configuration options?\nIf N, you will read -n 1 use_defaults if [ "$use_defaults" == "n" -o "$use_defaults" == "N" ]; then - #Give general guidance to user on how to answer prompts - echo - echo "****************************************************" - echo "**** Factory Reset / Re-Ownership Questionnaire ****" - echo "****************************************************" - echo "The following questionnaire will help you configure the security components of your system." - echo "Each prompt requires a single letter answer: eg. (Y/n)." - echo -e "If you don't know what to answer, pressing Enter will select the default answer for that prompt: eg. Y, above.\n" - - # Re-ownership of LUKS encrypted Disk: key, content and passphrase - echo -e -n "\n\nWould you like to change the current LUKS Disk Recovery Key passphrase?\n (Highly recommended if you didn't install the Operating System yourself, so that past configured passphrase would not permit to access content.\n Note that without re-encrypting disk, a backed up header could be restored to access encrypted content with old passphrase) [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ]; then - luks_new_Disk_Recovery_Key_passphrase_desired=1 - echo -e "\n" - fi - - echo -e -n "Would you like to re-encrypt LUKS encrypted container and generate new LUKS Disk Recovery Key?\n (Highly recommended if you didn't install the operating system yourself: this would prevent any LUKS backed up header to be restored to access encrypted data) [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ]; then - TRACE_FUNC - test_luks_current_disk_recovery_key_passphrase - luks_new_Disk_Recovery_Key_desired=1 - echo -e "\n" - fi - - #Prompt to ask if user wants to generate GPG key material in memory or on smartcard - echo -e -n "Would you like to format an encrypted USB Thumb drive to store GPG key material?\n (Required to enable GPG authentication) [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ] \ - ; then - GPG_GEN_KEY_IN_MEMORY="y" - echo " ++++ Master key and subkeys will be generated in memory, backed up to dedicated LUKS container +++" - echo -e -n "Would you like in-memory generated subkeys to be copied to USB Security Dongle's smartcard?\n (Highly recommended so the smartcard is used on daily basis and backup is kept safe, but not required) [Y/n]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "n" \ - -o "$prompt_output" == "N" ]; then - warn "Subkeys will NOT be copied to USB Security Dongle's smartcard" - warn "Your GPG key material backup thumb drive should be cloned to a second thumb drive for redundancy for production environements" - GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n" - else - echo "++++ Subkeys will be copied to USB Security Dongle's smartcard ++++" - warn "Please keep your GPG key material backup thumb drive safe" - GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="y" - fi - else - echo "GPG key material will be generated on USB Security Dongle's smartcard without backup" - GPG_GEN_KEY_IN_MEMORY="n" - GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n" - fi - - # Dynamic messages to be given to user in terms of security components that will be applied - # based on previous answers - CUSTOM_PASS_AFFECTED_COMPONENTS="\n" - # Adapt message to be given to user in terms of security components that will be applied. - if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -o -n "$luks_new_Disk_Recovery_Key_passphrase" ]; then - CUSTOM_PASS_AFFECTED_COMPONENTS+="LUKS Disk Recovery Key passphrase\n" - fi - if [ "$CONFIG_TPM" = "y" ]; then - CUSTOM_PASS_AFFECTED_COMPONENTS+="TPM Owner Password\n" - fi - if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then - CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG Key material backup passphrase (Same as GPG Admin PIN)\n" - fi - CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG Admin PIN\n" - # Only show GPG User PIN as affected component if GPG_GEN_KEY_IN_MEMORY not requested or GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is - if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then - CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG User PIN\n" - fi - - # Inform user of security components affected for the following prompts - echo - echo -e "The following Security Components will be configured with defaults or further chosen PINs/passwords: + #Give general guidance to user on how to answer prompts + echo + echo "****************************************************" + echo "**** Factory Reset / Re-Ownership Questionnaire ****" + echo "****************************************************" + echo "The following questionnaire will help you configure the security components of your system." + echo "Each prompt requires a single letter answer: eg. (Y/n)." + echo -e "If you don't know what to answer, pressing Enter will select the default answer for that prompt: eg. Y, above.\n" + + # Re-ownership of LUKS encrypted Disk: key, content and passphrase + echo -e -n "\n\nWould you like to change the current LUKS Disk Recovery Key passphrase?\n (Highly recommended if you didn't install the Operating System yourself, so that past configured passphrase would not permit to access content.\n Note that without re-encrypting disk, a backed up header could be restored to access encrypted content with old passphrase) [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ]; then + luks_new_Disk_Recovery_Key_passphrase_desired=1 + echo -e "\n" + fi + + echo -e -n "Would you like to re-encrypt LUKS encrypted container and generate new LUKS Disk Recovery Key?\n (Highly recommended if you didn't install the operating system yourself: this would prevent any LUKS backed up header to be restored to access encrypted data) [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ]; then + TRACE_FUNC + test_luks_current_disk_recovery_key_passphrase + luks_new_Disk_Recovery_Key_desired=1 + echo -e "\n" + fi + + #Prompt to ask if user wants to generate GPG key material in memory or on smartcard + echo -e -n "Would you like to format an encrypted USB Thumb drive to store GPG key material?\n (Required to enable GPG authentication) [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ] \ + ; then + GPG_GEN_KEY_IN_MEMORY="y" + echo " ++++ Master key and subkeys will be generated in memory, backed up to dedicated LUKS container +++" + echo -e -n "Would you like in-memory generated subkeys to be copied to USB Security Dongle's smartcard?\n (Highly recommended so the smartcard is used on daily basis and backup is kept safe, but not required) [Y/n]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "n" \ + -o "$prompt_output" == "N" ]; then + warn "Subkeys will NOT be copied to USB Security Dongle's smartcard" + warn "Your GPG key material backup thumb drive should be cloned to a second thumb drive for redundancy for production environements" + GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n" + else + echo "++++ Subkeys will be copied to USB Security Dongle's smartcard ++++" + warn "Please keep your GPG key material backup thumb drive safe" + GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="y" + fi + else + echo "GPG key material will be generated on USB Security Dongle's smartcard without backup" + GPG_GEN_KEY_IN_MEMORY="n" + GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n" + fi + + # Dynamic messages to be given to user in terms of security components that will be applied + # based on previous answers + CUSTOM_PASS_AFFECTED_COMPONENTS="\n" + # Adapt message to be given to user in terms of security components that will be applied. + if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -o -n "$luks_new_Disk_Recovery_Key_passphrase" ]; then + CUSTOM_PASS_AFFECTED_COMPONENTS+="LUKS Disk Recovery Key passphrase\n" + fi + if [ "$CONFIG_TPM" = "y" ]; then + CUSTOM_PASS_AFFECTED_COMPONENTS+="TPM Owner Password\n" + fi + if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then + CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG Key material backup passphrase (Same as GPG Admin PIN)\n" + fi + CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG Admin PIN\n" + # Only show GPG User PIN as affected component if GPG_GEN_KEY_IN_MEMORY not requested or GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is + if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then + CUSTOM_PASS_AFFECTED_COMPONENTS+="GPG User PIN\n" + fi + + # Inform user of security components affected for the following prompts + echo + echo -e "The following Security Components will be configured with defaults or further chosen PINs/passwords: $CUSTOM_PASS_AFFECTED_COMPONENTS\n" - # Prompt to change default passwords - echo -e -n "Would you like to set a single custom password to all previously stated security components? [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ]; then - echo -e "\nThe chosen custom password must be between 8 and $MAX_HOTP_GPG_PIN_LENGTH characters in length." - while [[ ${#CUSTOM_SINGLE_PASS} -lt 8 ]] || [[ ${#CUSTOM_SINGLE_PASS} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do - echo -e -n "Enter the custom password: " - read CUSTOM_SINGLE_PASS - done - echo - TPM_PASS=${CUSTOM_SINGLE_PASS} - USER_PIN=${CUSTOM_SINGLE_PASS} - ADMIN_PIN=${CUSTOM_SINGLE_PASS} - - # Only set if user said desired - if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then - luks_new_Disk_Recovery_Key_passphrase=${CUSTOM_SINGLE_PASS} - fi - else - echo -e -n "Would you like to set distinct PINs/passwords to configure previously stated security components? [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ]; then - echo -e "\nThe TPM Owner Password and Admin PIN must be at least 8, the User PIN at least 6 characters in length.\n" - echo - if [ "$CONFIG_TPM" = "y" ]; then - while [[ ${#TPM_PASS} -lt 8 ]]; do - echo -e -n "Enter desired TPM Owner Password: " - read TPM_PASS - done - fi - while [[ ${#ADMIN_PIN} -lt 6 ]] || [[ ${#ADMIN_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do - echo -e -n "\nThis PIN should be between 6 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n" - echo -e -n "Enter desired GPG Admin PIN: " - read ADMIN_PIN - done - #USER PIN not required in case of GPG_GEN_KEY_IN_MEMORY not requested of if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is - # That is, if keys were NOT generated in memory (on smartcard only) or - # if keys were generated in memory but are to be moved from local keyring to smartcard - if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then - while [[ ${#USER_PIN} -lt 6 ]] || [[ ${#USER_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do - echo -e -n "\nThis PIN should be between 6 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n" - echo -e -n "Enter desired GPG User PIN: " - read USER_PIN - done - fi - echo - fi - fi - - if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase" ]; then - # We catch here if changing LUKS Disk Recovery Key passphrase was desired - # but yet undone. This is if not being covered by the single password - echo -e "\nEnter desired replacement for current LUKS Disk Recovery Key passphrase (At least 8 characters long):" - while [[ ${#luks_new_Disk_Recovery_Key_passphrase} -lt 8 ]]; do - { - read -r luks_new_Disk_Recovery_Key_passphrase - } - done - #We test that current LUKS Disk Recovery Key passphrase is known prior of going further - TRACE_FUNC - test_luks_current_disk_recovery_key_passphrase - echo -e "\n" - fi - - # Prompt to change default GnuPG key information - echo -e -n "Would you like to set custom user information for the GnuPG key? [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ]; then - echo -e "\n\n" - echo -e "We will generate a GnuPG (PGP) keypair identifiable with the following text form:" - echo -e "Real Name (Comment) email@address.org" - - echo -e "\nEnter your Real Name (Optional):" - read -r GPG_USER_NAME - - echo -e "\nEnter your email@adress.org:" - read -r GPG_USER_MAIL - while ! $(expr "$GPG_USER_MAIL" : '.*@' >/dev/null); do - { - echo -e "\nEnter your email@address.org:" - read -r GPG_USER_MAIL - } - done - - echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):" - read -r GPG_USER_COMMENT - while [[ ${#GPG_USER_COMMENT} -gt 60 ]]; do - { - echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):" - read -r GPG_USER_COMMENT - } - done - fi - - if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then - select_thumb_drive_for_key_material - fi + # Prompt to change default passwords + echo -e -n "Would you like to set a single custom password to all previously stated security components? [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ]; then + echo -e "\nThe chosen custom password must be between 8 and $MAX_HOTP_GPG_PIN_LENGTH characters in length." + while [[ ${#CUSTOM_SINGLE_PASS} -lt 8 ]] || [[ ${#CUSTOM_SINGLE_PASS} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do + echo -e -n "Enter the custom password: " + read CUSTOM_SINGLE_PASS + done + echo + TPM_PASS=${CUSTOM_SINGLE_PASS} + USER_PIN=${CUSTOM_SINGLE_PASS} + ADMIN_PIN=${CUSTOM_SINGLE_PASS} + + # Only set if user said desired + if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then + luks_new_Disk_Recovery_Key_passphrase=${CUSTOM_SINGLE_PASS} + fi + else + echo -e -n "Would you like to set distinct PINs/passwords to configure previously stated security components? [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ]; then + echo -e "\nThe TPM Owner Password and Admin PIN must be at least 8, the User PIN at least 6 characters in length.\n" + echo + if [ "$CONFIG_TPM" = "y" ]; then + while [[ ${#TPM_PASS} -lt 8 ]]; do + echo -e -n "Enter desired TPM Owner Password: " + read TPM_PASS + done + fi + while [[ ${#ADMIN_PIN} -lt 6 ]] || [[ ${#ADMIN_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do + echo -e -n "\nThis PIN should be between 6 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n" + echo -e -n "Enter desired GPG Admin PIN: " + read ADMIN_PIN + done + #USER PIN not required in case of GPG_GEN_KEY_IN_MEMORY not requested of if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is + # That is, if keys were NOT generated in memory (on smartcard only) or + # if keys were generated in memory but are to be moved from local keyring to smartcard + if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then + while [[ ${#USER_PIN} -lt 6 ]] || [[ ${#USER_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]]; do + echo -e -n "\nThis PIN should be between 6 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n" + echo -e -n "Enter desired GPG User PIN: " + read USER_PIN + done + fi + echo + fi + fi + + if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase" ]; then + # We catch here if changing LUKS Disk Recovery Key passphrase was desired + # but yet undone. This is if not being covered by the single password + echo -e "\nEnter desired replacement for current LUKS Disk Recovery Key passphrase (At least 8 characters long):" + while [[ ${#luks_new_Disk_Recovery_Key_passphrase} -lt 8 ]]; do + { + read -r luks_new_Disk_Recovery_Key_passphrase + } + done + #We test that current LUKS Disk Recovery Key passphrase is known prior of going further + TRACE_FUNC + test_luks_current_disk_recovery_key_passphrase + echo -e "\n" + fi + + # Prompt to change default GnuPG key information + echo -e -n "Would you like to set custom user information for the GnuPG key? [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ]; then + echo -e "\n\n" + echo -e "We will generate a GnuPG (PGP) keypair identifiable with the following text form:" + echo -e "Real Name (Comment) email@address.org" + + echo -e "\nEnter your Real Name (Optional):" + read -r GPG_USER_NAME + + echo -e "\nEnter your email@adress.org:" + read -r GPG_USER_MAIL + while ! $(expr "$GPG_USER_MAIL" : '.*@' >/dev/null); do + { + echo -e "\nEnter your email@address.org:" + read -r GPG_USER_MAIL + } + done + + echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):" + read -r GPG_USER_COMMENT + while [[ ${#GPG_USER_COMMENT} -gt 60 ]]; do + { + echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):" + read -r GPG_USER_COMMENT + } + done + fi + + if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then + select_thumb_drive_for_key_material + fi fi # If nothing is stored in custom variables, we set them to their defaults @@ -1086,49 +1086,49 @@ if [ "$ADMIN_PIN" == "" ]; then ADMIN_PIN=${ADMIN_PIN_DEF}; fi ## sanity check the USB, GPG key, and boot device before proceeding further if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" ]; then - # Prompt to insert USB drive if desired - echo -e -n "\nWould you like to export your public key to an USB drive? [y/N]: " - read -n 1 prompt_output - echo - if [ "$prompt_output" == "y" \ - -o "$prompt_output" == "Y" ] \ - ; then - GPG_EXPORT=1 - # mount USB over /media only if not already mounted - if ! grep -q /media /proc/mounts; then - # mount USB in rw - if ! mount-usb --mode rw 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Unable to mount USB on /media:\n\n${ERROR}" - fi - else - #/media already mounted, make sure it is in r+w mode - if ! mount -o remount,rw /media 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Unable to remount in read+write USB on /media:\n\n${ERROR}" - fi - fi - else - GPG_EXPORT=0 - # needed for USB Security Dongle below and is ensured via mount-usb in case of GPG_EXPORT=1 - enable_usb - fi + # Prompt to insert USB drive if desired + echo -e -n "\nWould you like to export your public key to an USB drive? [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" \ + -o "$prompt_output" == "Y" ] \ + ; then + GPG_EXPORT=1 + # mount USB over /media only if not already mounted + if ! grep -q /media /proc/mounts; then + # mount USB in rw + if ! mount-usb --mode rw 2>/tmp/error; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Unable to mount USB on /media:\n\n${ERROR}" + fi + else + #/media already mounted, make sure it is in r+w mode + if ! mount -o remount,rw /media 2>/tmp/error; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Unable to remount in read+write USB on /media:\n\n${ERROR}" + fi + fi + else + GPG_EXPORT=0 + # needed for USB Security Dongle below and is ensured via mount-usb in case of GPG_EXPORT=1 + enable_usb + fi fi # ensure USB Security Dongle connected if GPG_GEN_KEY_IN_MEMORY=n or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD=y if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then - echo -e "\nChecking for USB Security Dongle...\n" - enable_usb - if ! gpg --card-status >/dev/null 2>&1; then - local_whiptail_error "Can't access USB Security Dongle; \nPlease remove and reinsert, then press Enter." - if ! gpg --card-status >/dev/null 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Unable to detect USB Security Dongle:\n\n${ERROR}" - fi - fi - - #Now that USB Security Dongle is detected, we can check its capabilities and limitations - usb_security_token_capabilities_check + echo -e "\nChecking for USB Security Dongle...\n" + enable_usb + if ! gpg --card-status >/dev/null 2>&1; then + local_whiptail_error "Can't access USB Security Dongle; \nPlease remove and reinsert, then press Enter." + if ! gpg --card-status >/dev/null 2>/tmp/error; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Unable to detect USB Security Dongle:\n\n${ERROR}" + fi + fi + + #Now that USB Security Dongle is detected, we can check its capabilities and limitations + usb_security_token_capabilities_check fi assert_signable @@ -1143,37 +1143,37 @@ rm -rf /.gnupg/*.kbx /.gnupg/*.gpg >/dev/null 2>&1 || true # detect and set /boot device echo -e "\nDetecting and setting boot device...\n" if ! detect_boot_device; then - SKIP_BOOT="y" + SKIP_BOOT="y" else - echo -e "Boot device set to $CONFIG_BOOT_DEV\n" + echo -e "Boot device set to $CONFIG_BOOT_DEV\n" fi # update configs if [[ "$SKIP_BOOT" == "n" ]]; then - replace_config /etc/config.user "CONFIG_BOOT_DEV" "$CONFIG_BOOT_DEV" - combine_configs + replace_config /etc/config.user "CONFIG_BOOT_DEV" "$CONFIG_BOOT_DEV" + combine_configs fi if [ -n "$luks_new_Disk_Recovery_Key_desired" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then - #Reencryption of disk, LUKS Disk Recovery Key and LUKS Disk Recovery Key passphrase change is requested - luks_reencrypt - luks_change_passphrase + #Reencryption of disk, LUKS Disk Recovery Key and LUKS Disk Recovery Key passphrase change is requested + luks_reencrypt + luks_change_passphrase elif [ -n "$luks_new_Disk_Recovery_Key_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then - #Reencryption of disk was requested but not passphrase change - luks_reencrypt + #Reencryption of disk was requested but not passphrase change + luks_reencrypt elif [ -z "$luks_new_Disk_Recovery_Key_desired" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then - #Passphrase change is requested without disk reencryption - luks_change_passphrase + #Passphrase change is requested without disk reencryption + luks_change_passphrase fi ## reset TPM and set password if [ "$CONFIG_TPM" = "y" ]; then - echo -e "\nResetting TPM...\n" - tpmr reset "$TPM_PASS" >/dev/null 2>/tmp/error + echo -e "\nResetting TPM...\n" + tpmr reset "$TPM_PASS" >/dev/null 2>/tmp/error fi if [ $? -ne 0 ]; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error resetting TPM:\n\n${ERROR}" + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error resetting TPM:\n\n${ERROR}" fi # clear local keyring @@ -1184,24 +1184,24 @@ gpg --list-keys >/dev/null 2>&1 #Generate keys in memory and copy to smartcard if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then - if [ "$GPG_ALGO" == "RSA" ]; then - # Generate GPG master key - generate_inmemory_RSA_master_and_subkeys - elif [ "$GPG_ALGO" == "p256" ]; then - generate_inmemory_p256_master_and_subkeys - else - die "Unsupported GPG_ALGO: $GPG_ALGO" - fi - wipe_thumb_drive_and_copy_gpg_key_material "$thumb_drive" "$thumb_drive_luks_percent" - set_user_config "CONFIG_HAVE_GPG_KEY_BACKUP" "y" - if [ "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then - keytocard_subkeys_to_smartcard - fi + if [ "$GPG_ALGO" == "RSA" ]; then + # Generate GPG master key + generate_inmemory_RSA_master_and_subkeys + elif [ "$GPG_ALGO" == "p256" ]; then + generate_inmemory_p256_master_and_subkeys + else + die "Unsupported GPG_ALGO: $GPG_ALGO" + fi + wipe_thumb_drive_and_copy_gpg_key_material "$thumb_drive" "$thumb_drive_luks_percent" + set_user_config "CONFIG_HAVE_GPG_KEY_BACKUP" "y" + if [ "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then + keytocard_subkeys_to_smartcard + fi else - #Generate GPG key and subkeys on smartcard only - echo -e "\nResetting USB Security Dongle's GPG smartcard...\n(this will take around 3 minutes...)\n" - gpg_key_factory_reset - generate_OEM_gpg_keys + #Generate GPG key and subkeys on smartcard only + echo -e "\nResetting USB Security Dongle's GPG smartcard...\n(this will take around 3 minutes...)\n" + gpg_key_factory_reset + generate_OEM_gpg_keys fi # Obtain GPG key ID @@ -1211,101 +1211,101 @@ PUBKEY="/tmp/${GPG_GEN_KEY}.asc" # export pubkey to file if ! gpg --export --armor "$GPG_GEN_KEY" >"${PUBKEY}" 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "GPG Key gpg export to file failed!\n\n$ERROR" + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "GPG Key gpg export to file failed!\n\n$ERROR" fi #Applying custom GPG PINs to the smartcard if they were provided if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then - #Only apply smartcard PIN change if smartcard only or if keytocard op is expected next - if [ "${USER_PIN}" != "" -o "${ADMIN_PIN}" != "" ]; then - echo -e "\nChanging default GPG Admin PIN\n" - gpg_key_change_pin "3" "${ADMIN_PIN_DEF}" "${ADMIN_PIN}" - echo -e "\nChanging default GPG User PIN\n" - gpg_key_change_pin "1" "${USER_PIN_DEF}" "${USER_PIN}" - fi + #Only apply smartcard PIN change if smartcard only or if keytocard op is expected next + if [ "${USER_PIN}" != "" -o "${ADMIN_PIN}" != "" ]; then + echo -e "\nChanging default GPG Admin PIN\n" + gpg_key_change_pin "3" "${ADMIN_PIN_DEF}" "${ADMIN_PIN}" + echo -e "\nChanging default GPG User PIN\n" + gpg_key_change_pin "1" "${USER_PIN_DEF}" "${USER_PIN}" + fi fi ## export pubkey to USB if [ "$GPG_EXPORT" != "0" ]; then - echo -e "\nExporting generated key to USB...\n" - # copy to USB - if ! cp "${PUBKEY}" "/media/${GPG_GEN_KEY}.asc" 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Key export error: unable to copy ${GPG_GEN_KEY}.asc to /media:\n\n$ERROR" - fi - mount -o remount,ro /media 2>/dev/null + echo -e "\nExporting generated key to USB...\n" + # copy to USB + if ! cp "${PUBKEY}" "/media/${GPG_GEN_KEY}.asc" 2>/tmp/error; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Key export error: unable to copy ${GPG_GEN_KEY}.asc to /media:\n\n$ERROR" + fi + mount -o remount,ro /media 2>/dev/null fi # ensure key imported locally if ! cat "$PUBKEY" | DO_WITH_DEBUG gpg --import >/dev/null 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error importing GPG key:\n\n$ERROR" + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error importing GPG key:\n\n$ERROR" fi # update /.gnupg/trustdb.gpg to ultimately trust all user provided public keys if ! gpg --list-keys --fingerprint --with-colons 2>/dev/null | - sed -E -n -e 's/^fpr:::::::::([0-9A-F]+):$/\1:6:/p' | - gpg --import-ownertrust >/dev/null 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error importing GPG ownertrust:\n\n$ERROR" + sed -E -n -e 's/^fpr:::::::::([0-9A-F]+):$/\1:6:/p' | + gpg --import-ownertrust >/dev/null 2>/tmp/error; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error importing GPG ownertrust:\n\n$ERROR" fi if ! gpg --update-trust >/dev/null 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR" + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR" fi # Do not attempt to flash the key to ROM if we are running in QEMU based on CONFIG_BOARD_NAME matching glob pattern containing qemu-* # We check for qemu-* instead of ^qemu- because CONFIG_BOARD_NAME could be renamed to UNTESTED-qemu-* in a probable future if [[ "$CONFIG_BOARD_NAME" == qemu-* ]]; then - warn "Skipping flash of GPG key to ROM because we are running in QEMU without internal flashing support." - warn "Please review boards/qemu*/qemu*.md documentation to extract public key from raw disk and inject at build time" - warn "Also review boards/qemu*/qemu*.config to tweak CONFIG_* options you might need to turn on/off manually at build time" + warn "Skipping flash of GPG key to ROM because we are running in QEMU without internal flashing support." + warn "Please review boards/qemu*/qemu*.md documentation to extract public key from raw disk and inject at build time" + warn "Also review boards/qemu*/qemu*.config to tweak CONFIG_* options you might need to turn on/off manually at build time" else - #We are not running in QEMU, so flash the key to ROM - - ## flash generated key to ROM - echo -e "\nReading current firmware...\n(this will take a minute or two)\n" - /bin/flash.sh -r /tmp/oem-setup.rom >/dev/null 2>/tmp/error - if [ ! -s /tmp/oem-setup.rom ]; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error reading current firmware:\n\n$ERROR" - fi - - # clear any existing heads/gpg files from current firmware - for i in $(cbfs.sh -o /tmp/oem-setup.rom -l | grep -e "heads/"); do - cbfs.sh -o /tmp/oem-setup.rom -d "$i" - done - # add heads/gpg files to current firmware - - if [ -e /.gnupg/pubring.kbx ]; then - cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx - if [ -e /.gnupg/pubring.gpg ]; then - rm /.gnupg/pubring.gpg - fi - elif [ -e /.gnupg/pubring.gpg ]; then - cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.gpg" -f /.gnupg/pubring.gpg - fi - if [ -e /.gnupg/trustdb.gpg ]; then - cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg - fi - - # persist user config changes (boot device) - if [ -e /etc/config.user ]; then - cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user - fi - - # flash updated firmware image - echo -e "\nAdding generated key to current firmware and re-flashing...\n" - if ! /bin/flash.sh /tmp/oem-setup.rom 2>/tmp/error; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR" - fi + #We are not running in QEMU, so flash the key to ROM + + ## flash generated key to ROM + echo -e "\nReading current firmware...\n(this will take a minute or two)\n" + /bin/flash.sh -r /tmp/oem-setup.rom >/dev/null 2>/tmp/error + if [ ! -s /tmp/oem-setup.rom ]; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error reading current firmware:\n\n$ERROR" + fi + + # clear any existing heads/gpg files from current firmware + for i in $(cbfs.sh -o /tmp/oem-setup.rom -l | grep -e "heads/"); do + cbfs.sh -o /tmp/oem-setup.rom -d "$i" + done + # add heads/gpg files to current firmware + + if [ -e /.gnupg/pubring.kbx ]; then + cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx + if [ -e /.gnupg/pubring.gpg ]; then + rm /.gnupg/pubring.gpg + fi + elif [ -e /.gnupg/pubring.gpg ]; then + cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.gpg" -f /.gnupg/pubring.gpg + fi + if [ -e /.gnupg/trustdb.gpg ]; then + cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg + fi + + # persist user config changes (boot device) + if [ -e /etc/config.user ]; then + cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user + fi + + # flash updated firmware image + echo -e "\nAdding generated key to current firmware and re-flashing...\n" + if ! /bin/flash.sh /tmp/oem-setup.rom 2>/tmp/error; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR" + fi fi ## sign files in /boot and generate checksums if [[ "$SKIP_BOOT" == "n" ]]; then - echo -e "\nSigning boot files and generating checksums...\n" - generate_checksums + echo -e "\nSigning boot files and generating checksums...\n" + generate_checksums fi # passphrases set to be empty first @@ -1313,29 +1313,29 @@ passphrases="\n" # Prepare whiptail output of configured secrets if [ -n "$luks_new_Disk_Recovery_Key_passphrase" -o -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then - passphrases+="LUKS Disk Recovery Key passphrase: ${luks_new_Disk_Recovery_Key_passphrase}\n" + passphrases+="LUKS Disk Recovery Key passphrase: ${luks_new_Disk_Recovery_Key_passphrase}\n" fi if [ "$CONFIG_TPM" = "y" ]; then - passphrases+="TPM Owner Password: ${TPM_PASS}\n" + passphrases+="TPM Owner Password: ${TPM_PASS}\n" fi #GPG PINs output passphrases+="GPG Admin PIN: ${ADMIN_PIN}\n" #USER PIN was configured if GPG_GEN_KEY_IN_MEMORY is not active or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is active if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then - passphrases+="GPG User PIN: ${USER_PIN}\n" + passphrases+="GPG User PIN: ${USER_PIN}\n" fi #If user decided to generate keys in memory, we add the thumb drive passphrase if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then - passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n" + passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n" fi ## Show to user current configured secrets prior of rebooting whiptail --msgbox " - $(echo -e "$passphrases" | fold -w $((WIDTH-5)))" \ - $HEIGHT $WIDTH --title "Configured secrets" + $(echo -e "$passphrases" | fold -w $((WIDTH - 5)))" \ + $HEIGHT $WIDTH --title "Configured secrets" ## all done -- reboot whiptail --msgbox " @@ -1343,7 +1343,7 @@ whiptail --msgbox " After rebooting, you will need to generate new TOTP/HOTP secrets\n when prompted in order to complete the setup process.\n\n Press Enter to reboot.\n" \ - $HEIGHT $WIDTH --title "OEM Factory Reset / Re-Ownership Complete" + $HEIGHT $WIDTH --title "OEM Factory Reset / Re-Ownership Complete" # Clean LUKS secrets luks_secrets_cleanup From 439f3eceb9025b293efe2f0a90d52b954a194ea0 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 17 Nov 2024 14:07:10 -0500 Subject: [PATCH 04/25] WiP initrd/bin/oem-factory-reset: add --mode (oem/user) skeleton Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 55 ++++++++++++++++++++++++++++++++++-- initrd/init | 4 +-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 533e7252b..135ecbedb 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -44,6 +44,45 @@ GPG_ALGO="RSA" # Default RSA key length is 3072 bits for OEM key gen. 4096 are way longer to generate in smartcard RSA_KEY_LENGTH=3072 +# Function to handle --mode parameter +handle_mode() { + local mode=$1 + case $mode in + oem) + DEBUG "OEM mode selected" + # Add OEM mode specific logic here + ;; + user) + DEBUG "User mode selected" + # Add User mode specific logic here + ;; + *) + warn "Unknown mode: $mode" + exit 1 + ;; + esac +} + +# Parse command-line arguments +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + --mode) + MODE="$2" + shift # past argument + shift # past value + ;; + *) + shift # past unrecognized argument + ;; + esac +done + +# Handle the --mode parameter if provided +if [[ -n "$MODE" ]]; then + handle_mode "$MODE" +fi + #Override RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards until canokey fixes if [[ "$CONFIG_BOARD_NAME" == qemu-* ]]; then DEBUG "Overriding RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards" @@ -1332,10 +1371,20 @@ if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n" fi -## Show to user current configured secrets prior of rebooting -whiptail --msgbox " +# Show qrcode of configured secrets and ask user to confirm scanning of and loop until confirmed with qrenc $passphrases +while true; do + whiptail --msgbox " $(echo -e "$passphrases" | fold -w $((WIDTH - 5)))" \ - $HEIGHT $WIDTH --title "Configured secrets" + $HEIGHT $WIDTH --title "Configured secrets" + qrencode "$passphrases" + # Prompt user to confirm scanning of qrcode on console prompt not whiptail: y/n + echo -e -n "Please confirm you have scanned the QR code above [y/N]: " + read -n 1 prompt_output + echo + if [ "$prompt_output" == "y" -o "$prompt_output" == "Y" ]; then + break + fi +done ## all done -- reboot whiptail --msgbox " diff --git a/initrd/init b/initrd/init index 55a894a79..3e7379e72 100755 --- a/initrd/init +++ b/initrd/init @@ -200,8 +200,8 @@ if [ "$boot_option" = "r" ]; then # just in case... exit elif [ "$boot_option" = "o" ]; then - # Launch OEM Factory Reset/Re-Ownership - oem-factory-reset + # Launch OEM Factory Reset mode + oem-factory-reset --mode oem # just in case... exit fi From 18c066f69764fbf49d520ce0453e2d7b5963360e Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 17 Nov 2024 17:36:21 -0500 Subject: [PATCH 05/25] /etc/functions:: reuse detect_boot_device instead of trying only to mount /etc/fstab existing /boot partition (otherwise early 'o' to enter oem mode of oem-factory-reset Signed-off-by: Thierry Laurion --- initrd/etc/functions | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/initrd/etc/functions b/initrd/etc/functions index 6a89dc60a..8bca8937d 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -570,9 +570,7 @@ escape_zero() { assert_signable() { TRACE_FUNC # ensure /boot mounted - if ! grep -q /boot /proc/mounts; then - mount -o ro /boot || die "Unable to mount /boot" - fi + detect_boot_device find /boot -print0 >/tmp/signable.ref local del='\001-\037\134\177-\377' From 89d15fb57cbee610c4305318fc86fbd79b5a5e70 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sun, 17 Nov 2024 17:37:30 -0500 Subject: [PATCH 06/25] WiP initrd/bin/oem-factory-reset: add qrcode+secet output loop until user press y (end of reownership wizard secret output) Signed-off-by: Thierry Laurion works: - oem and user mode passphrase generation - qrcode missing: - unattended - luks reencryption + passphrase change for OEM mode (only input to be provided) with SINGLE passphrase when in unattended mode - same for user reownership when previously OEM reset unattended Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 31 +++++++++++++++++++++---------- initrd/etc/functions | 16 ++++++---------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 135ecbedb..9b5f81422 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -23,12 +23,10 @@ CANCEL="--no-button Cancel" HEIGHT="0" WIDTH="80" +# Default values USER_PIN_DEF=123456 ADMIN_PIN_DEF=12345678 TPM_PASS_DEF=12345678 -USER_PIN="" -ADMIN_PIN="" -TPM_PASS="" GPG_GEN_KEY_IN_MEMORY="n" GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD="n" @@ -50,11 +48,16 @@ handle_mode() { case $mode in oem) DEBUG "OEM mode selected" - # Add OEM mode specific logic here + CUSTOM_SINGLE_PASS=$(generate_passphrase --number_words 2 --max_length $MAX_HOTP_GPG_PIN_LENGTH) + USER_PIN=$CUSTOM_SINGLE_PASS + ADMIN_PIN=$CUSTOM_SINGLE_PASS + TPM_PASS=$CUSTOM_SINGLE_PASS ;; user) DEBUG "User mode selected" - # Add User mode specific logic here + USER_PIN=$(generate_passphrase --number_words 2 --max_length $MAX_HOTP_GPG_PIN_LENGTH) + ADMIN_PIN=$(generate_passphrase --number_words 2 --max_length $MAX_HOTP_GPG_PIN_LENGTH) + TPM_PASS=$ADMIN_PIN ;; *) warn "Unknown mode: $mode" @@ -81,6 +84,9 @@ done # Handle the --mode parameter if provided if [[ -n "$MODE" ]]; then handle_mode "$MODE" +else + # Default to User Re-Ownership mode + handle_mode "user" fi #Override RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards until canokey fixes @@ -719,9 +725,10 @@ generate_checksums() { fi DEBUG "Detach-signing boot files under kexec.sig: ${param_files}" - if sha256sum $param_files 2>/dev/null | DO_WITH_DEBUG gpg \ + + if sha256sum $param_files 2>/dev/null | gpg \ --pinentry-mode loopback \ - --passphrase "${USER_PIN}" \ + --passphrase-file <(echo -n "$USER_PIN") \ --digest-algo SHA256 \ --detach-sign \ -a \ @@ -1371,14 +1378,18 @@ if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n" fi -# Show qrcode of configured secrets and ask user to confirm scanning of and loop until confirmed with qrenc $passphrases +# Show configured secrets in whiptail and loop until user confirms qr code was scanned while true; do whiptail --msgbox " $(echo -e "$passphrases" | fold -w $((WIDTH - 5)))" \ $HEIGHT $WIDTH --title "Configured secrets" - qrencode "$passphrases" + # strip the initial newline of passphrases + qr_code=$(echo -e "$passphrases" | sed '1s/^\n//') + #Tell user to scan the QR code containing all configured secrets + echo -e "\nScan the QR code below to save the secrets to a secure location" + qrenc "$qr_code" # Prompt user to confirm scanning of qrcode on console prompt not whiptail: y/n - echo -e -n "Please confirm you have scanned the QR code above [y/N]: " + echo -e -n "Please confirm you have scanned the QR code above and/or written down the secrets? [y/N]: " read -n 1 prompt_output echo if [ "$prompt_output" == "y" -o "$prompt_output" == "Y" ]; then diff --git a/initrd/etc/functions b/initrd/etc/functions index 8bca8937d..aa57676d9 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -887,7 +887,7 @@ generate_passphrase() { local dictionary_file="$2" local word="" - word=$(grep "^$rolls" "$dictionary_file" | awk '{print $2}') + word=$(grep "^$rolls" "$dictionary_file" | awk -F ' ' '{print $2}') echo "$word" } @@ -898,17 +898,14 @@ generate_passphrase() { local rolls="" local random_bytes - # Read num_rolls bytes from /dev/urandom in one go - random_bytes=$(dd if=/dev/urandom bs=1 count="$num_rolls" 2>/dev/null | hexdump -e '1/1 "%u\n"') + # Read num_rolls bytes from /dev/random, fed by CPU RRAND in one go + random_bytes=$(dd if=/dev/random bs=1 count="$num_rolls" 2>/dev/null | hexdump -e '1/1 "%u\n"') # Process each byte to generate a dice roll while read -r byte; do roll=$((byte % 6 + 1)) - DEBUG "Randomized dice roll: $roll" rolls+=$roll done <<<"$random_bytes" - - DEBUG "Generated dice rolls: $rolls" echo "$rolls" } @@ -978,15 +975,12 @@ generate_passphrase() { exit 1 fi - digits=${#key} - DEBUG "Number of digits in dice rolls: $digits" + digits=${#key} #Number of digits in dice rolls for ((i = 0; i < num_words; ++i)); do key=$(generate_dice_rolls "$digits") word=$(get_word_from_dictionary "$key" "$dictionary_file") - DEBUG "Retrieved word: $word" if [[ "$lowercase" == "false" ]]; then - DEBUG "Capitalizing the first letter of the word" word=${word^} # Capitalize the first letter fi passphrase+="$word " @@ -997,6 +991,8 @@ generate_passphrase() { fi done + #Remove passphrase trailing space from passphrase+="$word" + passphrase=${passphrase% } echo "$passphrase" return 0 } From 1e0df1f5975ba62fbfe33b7191e7c77cc8a0105b Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 28 Nov 2024 16:39:02 -0500 Subject: [PATCH 07/25] WiP: bump to hotp-verification version supporting reset of secret app Signed-off-by: Thierry Laurion --- modules/hotp-verification | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/hotp-verification b/modules/hotp-verification index 14957e74b..861579a69 100644 --- a/modules/hotp-verification +++ b/modules/hotp-verification @@ -2,12 +2,12 @@ modules-$(CONFIG_HOTPKEY) += hotp-verification hotp-verification_depends := libusb $(musl_dep) -# v1.6 -hotp-verification_version := e9050e0c914e7a8ffef5d1c82a014e0e2bf79346 +# v1.6 + patch from https://github.com/Nitrokey/nitrokey-hotp-verification/pull/46/commits/de355ed93ba50280bf377772082b76b7a2285185 +hotp-verification_version := de355ed93ba50280bf377772082b76b7a2285185 hotp-verification_dir := hotp-verification-$(hotp-verification_version) hotp-verification_tar := nitrokey-hotp-verification-$(hotp-verification_version).tar.gz hotp-verification_url := https://github.com/Nitrokey/nitrokey-hotp-verification/archive/$(hotp-verification_version).tar.gz -hotp-verification_hash := 480c978d3585eee73b9aa5186b471d4caeeeeba411217e1544eef7cfd90312ac +hotp-verification_hash := adbc2eb75257f4adda201419ed1282950a8cf86d167b2c76f708047d0b7aeaa5 hotp-verification_target := \ $(MAKE_JOBS) \ From c4832eed0e2dd49c3cbeab17f0e5471e40134f37 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 28 Nov 2024 16:57:26 -0500 Subject: [PATCH 08/25] WiP: add nk3 secret app reset function and call it following security dongle reset logic Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 9b5f81422..30717fe33 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -138,6 +138,17 @@ mount_boot() { fi } +reset_nk3_secret_app() { + TRACE_FUNC + # Reset Nitrokey 3 secret app + if lsusb | grep -q "20a0:42b2"; then + echo + echo "Resetting Nitrokey 3 secret app" + # Reset Nitrokey 3 secret app + /bin/hotp_verification reset + fi +} + #Generate a gpg master key: no expiration date, ${RSA_KEY_LENGTH} bits #This key will be used to sign 3 subkeys: encryption, authentication and signing #The master key and subkeys will be copied to backup, and the subkeys moved from memory keyring to the smartcard @@ -533,6 +544,11 @@ gpg_key_factory_reset() { ERROR=$(cat /tmp/gpg_card_edit_output) whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR" fi + + #Reset Nitrokey 3 secret app + reset_nk3_secret_app + # Nk3 now ready to set secret app PIN on first use... + # If Nitrokey Storage is inserted, reset AES keys as well if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then DEBUG "Nitrokey Storage detected, resetting AES keys..." @@ -540,6 +556,7 @@ gpg_key_factory_reset() { DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" killall -9 scdaemon fi + # Toggle forced sig (good security practice, forcing PIN request for each signature request) if gpg --card-status | grep "Signature PIN" | grep -q "not forced"; then DEBUG "GPG toggling forcesig on since off..." @@ -554,6 +571,7 @@ gpg_key_factory_reset() { whiptail_error_die "GPG Key forcesig toggle on failed!\n\n$ERROR" fi fi + # use p256 for key generation if requested if [ "$GPG_ALGO" = "p256" ]; then { From a9d3d96ec11452eb48cb360894c28ad029f6e38b Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 13:21:34 -0500 Subject: [PATCH 09/25] modules/hotp-verification: revert to 1.6, add patches tested instead Signed-off-by: Thierry Laurion --- modules/hotp-verification | 6 +- .../43.patch | 450 ++++++++++++++++++ .../46.patch | 219 +++++++++ 3 files changed, 672 insertions(+), 3 deletions(-) create mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch create mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch diff --git a/modules/hotp-verification b/modules/hotp-verification index 861579a69..14957e74b 100644 --- a/modules/hotp-verification +++ b/modules/hotp-verification @@ -2,12 +2,12 @@ modules-$(CONFIG_HOTPKEY) += hotp-verification hotp-verification_depends := libusb $(musl_dep) -# v1.6 + patch from https://github.com/Nitrokey/nitrokey-hotp-verification/pull/46/commits/de355ed93ba50280bf377772082b76b7a2285185 -hotp-verification_version := de355ed93ba50280bf377772082b76b7a2285185 +# v1.6 +hotp-verification_version := e9050e0c914e7a8ffef5d1c82a014e0e2bf79346 hotp-verification_dir := hotp-verification-$(hotp-verification_version) hotp-verification_tar := nitrokey-hotp-verification-$(hotp-verification_version).tar.gz hotp-verification_url := https://github.com/Nitrokey/nitrokey-hotp-verification/archive/$(hotp-verification_version).tar.gz -hotp-verification_hash := adbc2eb75257f4adda201419ed1282950a8cf86d167b2c76f708047d0b7aeaa5 +hotp-verification_hash := 480c978d3585eee73b9aa5186b471d4caeeeeba411217e1544eef7cfd90312ac hotp-verification_target := \ $(MAKE_JOBS) \ diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch new file mode 100644 index 000000000..26ad2bc45 --- /dev/null +++ b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch @@ -0,0 +1,450 @@ +From 707c6545a509eeb24a06537e5f835d786c2e657e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Thu, 5 Dec 2024 16:44:30 +0100 +Subject: [PATCH] Add support for nitrokey 3 distinction between the secrets + app and other + +This now adds the secrets app version and the nitrokey 3 firmware version, +and also the gpg pins +--- + src/ccid.c | 71 +++++++++++++++++++++++++- + src/ccid.h | 2 + + src/device.c | 29 +++++------ + src/device.h | 2 +- + src/main.c | 41 +++++++++++---- + src/operations_ccid.c | 113 +++++++++++++++++++++++++++++++++++++----- + src/operations_ccid.h | 2 +- + src/structs.h | 19 +++++++ + 8 files changed, 238 insertions(+), 41 deletions(-) + +diff --git a/src/ccid.c b/src/ccid.c +index 9cf24a0..a2cc919 100644 +--- a/src/ccid.c ++++ b/src/ccid.c +@@ -104,7 +104,7 @@ IccResult parse_icc_result(uint8_t *buf, size_t buf_len) { + // .buffer_len = buf_len + }; + // Make sure the response do not contain overread attempts +- rassert(i.data_len < buf_len - 10); ++ rassert(i.data_len <= buf_len - 10); + return i; + } + +@@ -307,6 +307,75 @@ int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_siz + return RET_NO_ERROR; + } + ++int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { ++ unsigned char cmd_select[] = { ++ 0x6f, ++ 0x0E, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0xa4, ++ 0x04, ++ 0x00, ++ 0x09, ++ 0xa0, ++ 0x00, ++ 0x00, ++ 0x08, ++ 0x47, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x01, ++ }; ++ ++ check_ret( ++ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), ++ RET_COMM_ERROR); ++ ++ ++ return RET_NO_ERROR; ++} ++ ++int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { ++ unsigned char cmd_select[] = { ++ 0x6f, ++ 0x0C, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0xA4, ++ 0x04, ++ 0x00, ++ 0x06, ++ 0xD2, ++ 0x76, ++ 0x00, ++ 0x01, ++ 0x24, ++ 0x01, ++ 0x00, ++ }; ++ ++ check_ret( ++ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), ++ RET_COMM_ERROR); ++ ++ ++ return RET_NO_ERROR; ++} + + int ccid_init(libusb_device_handle *handle) { + +diff --git a/src/ccid.h b/src/ccid.h +index ed17dc7..3dcf106 100644 +--- a/src/ccid.h ++++ b/src/ccid.h +@@ -70,6 +70,8 @@ uint32_t icc_pack_tlvs_for_sending(uint8_t *buf, size_t buflen, TLV tlvs[], int + libusb_device_handle *get_device(libusb_context *ctx, const struct VidPid pPid[], int devices_count); + int ccid_init(libusb_device_handle *handle); + int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); ++int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); ++int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); + + + enum { +diff --git a/src/device.c b/src/device.c +index 4b9361e..52acece 100644 +--- a/src/device.c ++++ b/src/device.c +@@ -29,6 +29,7 @@ + #include "structs.h" + #include "utils.h" + #include ++#include + #include + #include + #include +@@ -259,23 +260,19 @@ int device_receive_buf(struct Device *dev) { + + #include "operations_ccid.h" + +-int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { +- assert(out_status != NULL); ++int device_get_status(struct Device *dev, struct FullResponseStatus *out_response) { ++ assert(out_response != NULL); + assert(dev != NULL); +- memset(out_status, 0, sizeof(struct ResponseStatus)); ++ memset(out_response, 0, sizeof(struct FullResponseStatus)); ++ ++ struct ResponseStatus *out_status = &out_response->response_status; + + if (dev->connection_type == CONNECTION_CCID) { +- int counter = 0; +- uint32_t serial = 0; +- uint16_t version = 0; +- int res = status_ccid(dev->mp_devhandle_ccid, +- &counter, +- &version, +- &serial); +- out_status->retry_admin = counter; +- out_status->retry_user = counter; +- out_status->card_serial_u32 = serial; +- out_status->firmware_version = version; ++ int res = status_ccid(dev->mp_devhandle_ccid, out_response); ++ // out_status->retry_admin = counter; ++ // out_status->retry_user = counter; ++ // out_status->card_serial_u32 = serial; ++ // out_status->firmware_version = version; + return res; + } + +@@ -290,7 +287,7 @@ int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { + + device_send_buf(dev, GET_STATUS); + device_receive_buf(dev); +- *out_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; ++ out_response->response_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; + + if (out_status->firmware_version_st.minor == 1) { + for (int i = 0; i < 100; ++i) { +@@ -343,4 +340,4 @@ const char *command_status_to_string(uint8_t status_code) { + void clean_buffers(struct Device *dev) { + memset(dev->ccid_buffer_in, 0, sizeof dev->ccid_buffer_in); + memset(dev->ccid_buffer_out, 0, sizeof dev->ccid_buffer_out); +-} +\ No newline at end of file ++} +diff --git a/src/device.h b/src/device.h +index c895546..97feeeb 100644 +--- a/src/device.h ++++ b/src/device.h +@@ -72,7 +72,7 @@ struct Device { + + int device_connect(struct Device *dev); + int device_disconnect(struct Device *dev); +-int device_get_status(struct Device *dev, struct ResponseStatus *out_status); ++int device_get_status(struct Device *dev, struct FullResponseStatus *out_status); + int device_send(struct Device *dev, uint8_t *in_data, size_t data_size, uint8_t command_ID); + int device_receive(struct Device *dev, uint8_t *out_data, size_t out_buffer_size); + int device_send_buf(struct Device *dev, uint8_t command_ID); +diff --git a/src/main.c b/src/main.c +index 059069e..9b38552 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -93,25 +93,46 @@ int parse_cmd_and_run(int argc, char *const *argv) { + res = RET_NO_ERROR; + break; + case 'i': {// id | info +- struct ResponseStatus status; ++ struct FullResponseStatus status; ++ memset(&status, 0, sizeof (struct FullResponseStatus)); ++ + res = device_get_status(&dev, &status); + check_ret((res != RET_NO_ERROR) && (res != RET_NO_PIN_ATTEMPTS), res); + if (strnlen(argv[1], 10) == 2 && argv[1][1] == 'd') { + // id command - print ID only +- print_card_serial(&status); ++ print_card_serial(&status.response_status); + } else { + // info command - print status + printf("Connected device status:\n"); + printf("\tCard serial: "); +- print_card_serial(&status); +- printf("\tFirmware: v%d.%d\n", +- status.firmware_version_st.major, +- status.firmware_version_st.minor); +- if (res != RET_NO_PIN_ATTEMPTS) { +- printf("\tCard counters: Admin %d, User %d\n", +- status.retry_admin, status.retry_user); ++ print_card_serial(&status.response_status); ++ if (status.device_type == Nk3) { ++ printf("\tFirmware Nitrokey 3: v%d.%d.%d\n", ++ (status.nk3_extra_info.firmware_version >> 22) & 0b1111111111, ++ (status.nk3_extra_info.firmware_version >> 6) & 0xFFFF, ++ status.nk3_extra_info.firmware_version & 0b111111); ++ printf("\tFirmware Secrets App: v%d.%d\n", ++ status.response_status.firmware_version_st.major, ++ status.response_status.firmware_version_st.minor); ++ if (res != RET_NO_PIN_ATTEMPTS) { ++ printf("\tSecrets app PIN counter: %d\n", ++ status.response_status.retry_user); ++ } else { ++ printf("\tSecrets app PIN counter: PIN is not set - set PIN before the first use\n"); ++ } ++ printf("\tGPG Card counters: Admin %d, User %d\n", ++ status.nk3_extra_info.pgp_admin_pin_retries, ++ status.nk3_extra_info.pgp_user_pin_retries); + } else { +- printf("\tCard counters: PIN is not set - set PIN before the first use\n"); ++ printf("\tFirmware: v%d.%d\n", ++ status.response_status.firmware_version_st.major, ++ status.response_status.firmware_version_st.minor); ++ if (res != RET_NO_PIN_ATTEMPTS) { ++ printf("\tCard counters: Admin %d, User %d\n", ++ status.response_status.retry_admin, status.response_status.retry_user); ++ } else { ++ printf("\tCard counters: PIN is not set - set PIN before the first use\n"); ++ } + } + } + if (res == RET_NO_PIN_ATTEMPTS) { +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index eb46124..25772e5 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -273,14 +273,102 @@ int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify) { + return RET_VALIDATION_PASSED; + } + +-int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number) { ++int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response) { ++ rassert(full_response != NULL); ++ struct ResponseStatus *response = &full_response->response_status; + rassert(handle != NULL); +- rassert(attempt_counter != NULL); +- rassert(firmware_version != NULL); +- rassert(serial_number != NULL); + uint8_t buf[1024] = {}; + IccResult iccResult = {}; +- int r = send_select_ccid(handle, buf, sizeof buf, &iccResult); ++ bool pin_counter_is_error = false; ++ int r; ++ libusb_device *usb_dev; ++ struct libusb_device_descriptor usb_desc; ++ ++ usb_dev = libusb_get_device(handle); ++ ++ r = libusb_get_device_descriptor(usb_dev, &usb_desc); ++ ++ if (r < 0) { ++ return r; ++ } ++ ++ ++ if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_3_USB_PID) { ++ full_response->device_type = Nk3; ++ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_PRO_USB_PID) { ++ full_response->device_type = NkPro2; ++ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_STORAGE_USB_PID) { ++ full_response->device_type = NkStorage; ++ } else if (usb_desc.idVendor == LIBREM_KEY_USB_VID || usb_desc.idProduct == LIBREM_KEY_USB_PID) { ++ full_response->device_type = LibremKey; ++ } ++ ++ if (full_response->device_type == Nk3) { ++ r = send_select_nk3_admin_ccid(handle, buf, sizeof buf, &iccResult); ++ if (r != RET_NO_ERROR) { ++ return r; ++ } ++ ++ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; ++ uint32_t iso_actual_length = iso7816_compose( ++ data_iso, sizeof data_iso, ++ 0x61, 0, 0, 0, 4, NULL, 0); ++ ++ // encode ccid wrapper ++ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, ++ 0x6F, iso_actual_length, ++ 0, 0, 0, data_iso); ++ int transferred; ++ r = ccid_send(handle, &transferred, buf, icc_actual_length); ++ if (r != 0) { ++ return r; ++ } ++ ++ r = ccid_receive(handle, &transferred, buf, sizeof buf); ++ if (r != 0) { ++ return r; ++ } ++ ++ IccResult iccResult = parse_icc_result(buf, transferred); ++ rassert(iccResult.data_status_code == 0x9000); ++ rassert(iccResult.data_len == 6); ++ full_response->nk3_extra_info.firmware_version = be32toh(*(uint32_t *) iccResult.data); ++ } ++ ++ if (full_response->device_type == Nk3) { ++ r = send_select_nk3_pgp_ccid(handle, buf, sizeof buf, &iccResult); ++ if (r != RET_NO_ERROR) { ++ return r; ++ } ++ ++ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; ++ uint32_t iso_actual_length = iso7816_compose( ++ data_iso, sizeof data_iso, ++ 0xCA, 0, 0xC4, 0, 0xFF, NULL, 0); ++ ++ // encode ccid wrapper ++ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, ++ 0x6F, iso_actual_length, ++ 0, 0, 0, data_iso); ++ int transferred; ++ r = ccid_send(handle, &transferred, buf, icc_actual_length); ++ if (r != 0) { ++ return r; ++ } ++ ++ r = ccid_receive(handle, &transferred, buf, sizeof buf); ++ if (r != 0) { ++ return r; ++ } ++ ++ IccResult iccResult = parse_icc_result(buf, transferred); ++ rassert(iccResult.data_status_code == 0x9000); ++ rassert(iccResult.data_len == 9); ++ full_response->nk3_extra_info.pgp_user_pin_retries = iccResult.data[4]; ++ full_response->nk3_extra_info.pgp_admin_pin_retries = iccResult.data[6]; ++ } ++ ++ r = send_select_ccid(handle, buf, sizeof buf, &iccResult); + if (r != RET_NO_ERROR) { + return r; + } +@@ -292,29 +380,30 @@ int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *fi + r = get_tlv(iccResult.data, iccResult.data_len, Tag_PINCounter, &counter_tlv); + if (!(r == RET_NO_ERROR && counter_tlv.tag == Tag_PINCounter)) { + // PIN counter not found - comm error (ignore) or PIN not set +- *attempt_counter = -1; ++ pin_counter_is_error = true; + } else { +- *attempt_counter = counter_tlv.v_data[0]; ++ response->retry_admin = counter_tlv.v_data[0]; ++ response->retry_user = counter_tlv.v_data[0]; + } + + TLV serial_tlv = {}; + r = get_tlv(iccResult.data, iccResult.data_len, Tag_SerialNumber, &serial_tlv); + if (r == RET_NO_ERROR && serial_tlv.tag == Tag_SerialNumber) { +- *serial_number = be32toh(*(uint32_t *) serial_tlv.v_data); ++ response->card_serial_u32 = be32toh(*(uint32_t *) serial_tlv.v_data); + } else { + // ignore errors - unsupported or hidden serial_tlv number +- *serial_number = 0; ++ response->card_serial_u32 = 0; + } + + TLV version_tlv = {}; + r = get_tlv(iccResult.data, iccResult.data_len, Tag_Version, &version_tlv); + if (!(r == RET_NO_ERROR && version_tlv.tag == Tag_Version)) { +- *firmware_version = 0; ++ response->firmware_version = 0; + return RET_COMM_ERROR; + } +- *firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); ++ response->firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); + +- if (*attempt_counter == -1) { ++ if (pin_counter_is_error == true) { + return RET_NO_PIN_ATTEMPTS; + } + return RET_NO_ERROR; +diff --git a/src/operations_ccid.h b/src/operations_ccid.h +index b26b3c7..ea463b4 100644 +--- a/src/operations_ccid.h ++++ b/src/operations_ccid.h +@@ -10,7 +10,7 @@ int authenticate_ccid(struct Device *dev, const char *admin_PIN); + int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); + int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); + int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); +-int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); ++int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response); + + + #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H +diff --git a/src/structs.h b/src/structs.h +index 6309cd0..9e87134 100644 +--- a/src/structs.h ++++ b/src/structs.h +@@ -116,6 +116,25 @@ struct ResponseStatus { + uint8_t retry_user; /*not present in the firmware response for the Status command in v0.8 firmware*/ + }; + ++enum DeviceType { ++ Unknown = 0, ++ Nk3, ++ NkPro2, ++ NkStorage, ++ LibremKey, ++}; ++ ++struct FullResponseStatus { ++ enum DeviceType device_type; ++ struct ResponseStatus response_status; ++ struct { ++ // Only valid if device_type is NK3 ++ uint8_t pgp_admin_pin_retries; ++ uint8_t pgp_user_pin_retries; ++ uint32_t firmware_version; ++ } nk3_extra_info; ++}; ++ + + struct WriteToOTPSlot { + //admin auth diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch new file mode 100644 index 000000000..781c10ffa --- /dev/null +++ b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch @@ -0,0 +1,219 @@ +From de355ed93ba50280bf377772082b76b7a2285185 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Mon, 25 Nov 2024 17:04:47 +0100 +Subject: [PATCH 1/3] Add reset command for nitrokey 3 + +--- + src/main.c | 10 ++++++++-- + src/operations_ccid.c | 41 +++++++++++++++++++++++++++++++++++++++++ + src/operations_ccid.h | 1 + + 3 files changed, 50 insertions(+), 2 deletions(-) + +diff --git a/src/main.c b/src/main.c +index 059069e..b80b71d 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -21,6 +21,7 @@ + + #include "ccid.h" + #include "operations.h" ++#include "operations_ccid.h" + #include "return_codes.h" + #include "utils.h" + #include "version.h" +@@ -134,8 +135,13 @@ int parse_cmd_and_run(int argc, char *const *argv) { + } + break; + case 'r': +- if (argc != 3) break; +- res = regenerate_AES_key(&dev, argv[2]); ++ if (strncmp(argv[1], "reset", 15) == 0) { ++ if (argc != 2) break; ++ res = nk3_reset(&dev); ++ } else if (strncmp(argv[1], "regenerate", 15) == 0) { ++ if (argc != 3) break; ++ res = regenerate_AES_key(&dev, argv[2]); ++ } + break; + default: + break; +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index eb46124..574155d 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -32,6 +32,47 @@ + #include + + ++ ++int nk3_reset(struct Device *dev) { ++ libusb_device *usb_dev; ++ struct libusb_device_descriptor usb_desc; ++ usb_dev = libusb_get_device(dev->mp_devhandle_ccid); ++ ++ int r = libusb_get_device_descriptor(usb_dev, &usb_desc); ++ ++ if (r < 0) { ++ return r; ++ } ++ ++ ++ if (usb_desc.idVendor != NITROKEY_USB_VID || usb_desc.idProduct != NITROKEY_3_USB_PID) { ++ return 0; ++ } ++ ++ ++ uint8_t buf[10]; ++ // encode ++ uint32_t icc_actual_length = iso7816_compose(buf, sizeof buf, Ins_Reset, 0xDE, 0xAD, 0, 0, NULL, 0); ++ ++ // encode ccid wrapper ++ icc_actual_length = icc_compose(dev->ccid_buffer_out, sizeof dev->ccid_buffer_out, ++ 0x6F, icc_actual_length, ++ 0, 0, 0, buf); ++ // send ++ IccResult iccResult; ++ r = ccid_process_single(dev->mp_devhandle_ccid, dev->ccid_buffer_in, sizeof dev->ccid_buffer_in, ++ dev->ccid_buffer_out, icc_actual_length, &iccResult); ++ if (r != 0) { ++ return r; ++ } ++ // check status code ++ if (iccResult.data_status_code != 0x9000) { ++ return 1; ++ } ++ ++ return RET_NO_ERROR; ++} ++ + int set_pin_ccid(struct Device *dev, const char *admin_PIN) { + TLV tlvs[] = { + { +diff --git a/src/operations_ccid.h b/src/operations_ccid.h +index b26b3c7..ec0070c 100644 +--- a/src/operations_ccid.h ++++ b/src/operations_ccid.h +@@ -11,6 +11,7 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); + int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); + int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); + int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); ++int nk3_reset(struct Device *dev); + + + #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H + +From 8425e8c622138aef9ab207119e14f7cbedd40175 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Mon, 2 Dec 2024 10:29:59 +0100 +Subject: [PATCH 2/3] Add optional new pin when resetting + +--- + src/main.c | 9 +++++---- + src/operations_ccid.c | 6 +++++- + src/operations_ccid.h | 5 ++++- + 3 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/src/main.c b/src/main.c +index b80b71d..3f4a1cc 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -38,9 +38,10 @@ void print_help(char *app_name) { + "\t%s info\n" + "\t%s version\n" + "\t%s check \n" +- "\t%s regenerate \n" ++ "\t%s reset [ADMIN PIN]\n" ++ "\t%s regenerate\n" + "\t%s set [COUNTER]\n", +- app_name, app_name, app_name, app_name, app_name, app_name); ++ app_name, app_name, app_name, app_name, app_name, app_name, app_name); + } + + +@@ -136,8 +137,8 @@ int parse_cmd_and_run(int argc, char *const *argv) { + break; + case 'r': + if (strncmp(argv[1], "reset", 15) == 0) { +- if (argc != 2) break; +- res = nk3_reset(&dev); ++ if (argc != 2 && argc != 3) break; ++ res = nk3_reset(&dev, argc == 3 ? argv[2]: NULL); + } else if (strncmp(argv[1], "regenerate", 15) == 0) { + if (argc != 3) break; + res = regenerate_AES_key(&dev, argv[2]); +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index 574155d..07834ce 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -33,7 +33,7 @@ + + + +-int nk3_reset(struct Device *dev) { ++int nk3_reset(struct Device *dev, const char * new_pin) { + libusb_device *usb_dev; + struct libusb_device_descriptor usb_desc; + usb_dev = libusb_get_device(dev->mp_devhandle_ccid); +@@ -70,6 +70,10 @@ int nk3_reset(struct Device *dev) { + return 1; + } + ++ if (new_pin != NULL) { ++ set_pin_ccid(dev, new_pin); ++ } ++ + return RET_NO_ERROR; + } + +diff --git a/src/operations_ccid.h b/src/operations_ccid.h +index ec0070c..61cad72 100644 +--- a/src/operations_ccid.h ++++ b/src/operations_ccid.h +@@ -11,7 +11,10 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); + int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); + int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); + int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); +-int nk3_reset(struct Device *dev); ++// new_pin can be `null` ++// ++// If it is, no new pin will be set ++int nk3_reset(struct Device *dev, const char * new_pin); + + + #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H + +From 596f701985682adf6bfab06c78cbe132cbcb2aae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Tue, 3 Dec 2024 10:48:27 +0100 +Subject: [PATCH 3/3] Fix null pointer bug on non nk3 + +--- + src/operations_ccid.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index 07834ce..538d434 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -36,6 +36,12 @@ + int nk3_reset(struct Device *dev, const char * new_pin) { + libusb_device *usb_dev; + struct libusb_device_descriptor usb_desc; ++ ++ if (!dev->mp_devhandle_ccid) { ++ // Not an NK3 ++ return RET_NO_ERROR; ++ } ++ + usb_dev = libusb_get_device(dev->mp_devhandle_ccid); + + int r = libusb_get_device_descriptor(usb_dev, &usb_desc); +@@ -46,7 +52,7 @@ int nk3_reset(struct Device *dev, const char * new_pin) { + + + if (usb_desc.idVendor != NITROKEY_USB_VID || usb_desc.idProduct != NITROKEY_3_USB_PID) { +- return 0; ++ return RET_NO_ERROR; + } + + From b550151d54fb8545b16ff4e6c9a2e2eb57f0d289 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 13:23:37 -0500 Subject: [PATCH 10/25] oem-factory-reset: add reset secure app PIN = ADMIN_PIN at reownership, make sure defaults are set for all modes, including default which uses current defaults being DEF pins (12345678 and 123456 as master) Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 30717fe33..7c0340aa7 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -60,8 +60,10 @@ handle_mode() { TPM_PASS=$ADMIN_PIN ;; *) - warn "Unknown mode: $mode" - exit 1 + warn "Unknown oem-factory-reset lauched mode, setting PINs to weak defaults" + USER_PIN=$USER_PIN_DEF + ADMIN_PIN=$ADMIN_PIN_DEF + TPM_PASS=$ADMIN_PIN_DEF ;; esac } @@ -144,8 +146,10 @@ reset_nk3_secret_app() { if lsusb | grep -q "20a0:42b2"; then echo echo "Resetting Nitrokey 3 secret app" + DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" + killall -9 scdaemon 2>&1 >/dev/null || true # Reset Nitrokey 3 secret app - /bin/hotp_verification reset + /bin/hotp_verification reset $ADMIN_PIN fi } From 9623053da510f6ef22bf1749719812d673d3e0c3 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 13:32:23 -0500 Subject: [PATCH 11/25] modules/hotp-verification: 1.6, removing patch pr43, only keeping 46 for this PR (43 conflicts when applied atop 46. 46 is needed here) Signed-off-by: Thierry Laurion --- .../43.patch | 450 ------------------ 1 file changed, 450 deletions(-) delete mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch deleted file mode 100644 index 26ad2bc45..000000000 --- a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch +++ /dev/null @@ -1,450 +0,0 @@ -From 707c6545a509eeb24a06537e5f835d786c2e657e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= -Date: Thu, 5 Dec 2024 16:44:30 +0100 -Subject: [PATCH] Add support for nitrokey 3 distinction between the secrets - app and other - -This now adds the secrets app version and the nitrokey 3 firmware version, -and also the gpg pins ---- - src/ccid.c | 71 +++++++++++++++++++++++++- - src/ccid.h | 2 + - src/device.c | 29 +++++------ - src/device.h | 2 +- - src/main.c | 41 +++++++++++---- - src/operations_ccid.c | 113 +++++++++++++++++++++++++++++++++++++----- - src/operations_ccid.h | 2 +- - src/structs.h | 19 +++++++ - 8 files changed, 238 insertions(+), 41 deletions(-) - -diff --git a/src/ccid.c b/src/ccid.c -index 9cf24a0..a2cc919 100644 ---- a/src/ccid.c -+++ b/src/ccid.c -@@ -104,7 +104,7 @@ IccResult parse_icc_result(uint8_t *buf, size_t buf_len) { - // .buffer_len = buf_len - }; - // Make sure the response do not contain overread attempts -- rassert(i.data_len < buf_len - 10); -+ rassert(i.data_len <= buf_len - 10); - return i; - } - -@@ -307,6 +307,75 @@ int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_siz - return RET_NO_ERROR; - } - -+int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { -+ unsigned char cmd_select[] = { -+ 0x6f, -+ 0x0E, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0xa4, -+ 0x04, -+ 0x00, -+ 0x09, -+ 0xa0, -+ 0x00, -+ 0x00, -+ 0x08, -+ 0x47, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x01, -+ }; -+ -+ check_ret( -+ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), -+ RET_COMM_ERROR); -+ -+ -+ return RET_NO_ERROR; -+} -+ -+int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { -+ unsigned char cmd_select[] = { -+ 0x6f, -+ 0x0C, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0xA4, -+ 0x04, -+ 0x00, -+ 0x06, -+ 0xD2, -+ 0x76, -+ 0x00, -+ 0x01, -+ 0x24, -+ 0x01, -+ 0x00, -+ }; -+ -+ check_ret( -+ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), -+ RET_COMM_ERROR); -+ -+ -+ return RET_NO_ERROR; -+} - - int ccid_init(libusb_device_handle *handle) { - -diff --git a/src/ccid.h b/src/ccid.h -index ed17dc7..3dcf106 100644 ---- a/src/ccid.h -+++ b/src/ccid.h -@@ -70,6 +70,8 @@ uint32_t icc_pack_tlvs_for_sending(uint8_t *buf, size_t buflen, TLV tlvs[], int - libusb_device_handle *get_device(libusb_context *ctx, const struct VidPid pPid[], int devices_count); - int ccid_init(libusb_device_handle *handle); - int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); -+int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); -+int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); - - - enum { -diff --git a/src/device.c b/src/device.c -index 4b9361e..52acece 100644 ---- a/src/device.c -+++ b/src/device.c -@@ -29,6 +29,7 @@ - #include "structs.h" - #include "utils.h" - #include -+#include - #include - #include - #include -@@ -259,23 +260,19 @@ int device_receive_buf(struct Device *dev) { - - #include "operations_ccid.h" - --int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { -- assert(out_status != NULL); -+int device_get_status(struct Device *dev, struct FullResponseStatus *out_response) { -+ assert(out_response != NULL); - assert(dev != NULL); -- memset(out_status, 0, sizeof(struct ResponseStatus)); -+ memset(out_response, 0, sizeof(struct FullResponseStatus)); -+ -+ struct ResponseStatus *out_status = &out_response->response_status; - - if (dev->connection_type == CONNECTION_CCID) { -- int counter = 0; -- uint32_t serial = 0; -- uint16_t version = 0; -- int res = status_ccid(dev->mp_devhandle_ccid, -- &counter, -- &version, -- &serial); -- out_status->retry_admin = counter; -- out_status->retry_user = counter; -- out_status->card_serial_u32 = serial; -- out_status->firmware_version = version; -+ int res = status_ccid(dev->mp_devhandle_ccid, out_response); -+ // out_status->retry_admin = counter; -+ // out_status->retry_user = counter; -+ // out_status->card_serial_u32 = serial; -+ // out_status->firmware_version = version; - return res; - } - -@@ -290,7 +287,7 @@ int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { - - device_send_buf(dev, GET_STATUS); - device_receive_buf(dev); -- *out_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; -+ out_response->response_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; - - if (out_status->firmware_version_st.minor == 1) { - for (int i = 0; i < 100; ++i) { -@@ -343,4 +340,4 @@ const char *command_status_to_string(uint8_t status_code) { - void clean_buffers(struct Device *dev) { - memset(dev->ccid_buffer_in, 0, sizeof dev->ccid_buffer_in); - memset(dev->ccid_buffer_out, 0, sizeof dev->ccid_buffer_out); --} -\ No newline at end of file -+} -diff --git a/src/device.h b/src/device.h -index c895546..97feeeb 100644 ---- a/src/device.h -+++ b/src/device.h -@@ -72,7 +72,7 @@ struct Device { - - int device_connect(struct Device *dev); - int device_disconnect(struct Device *dev); --int device_get_status(struct Device *dev, struct ResponseStatus *out_status); -+int device_get_status(struct Device *dev, struct FullResponseStatus *out_status); - int device_send(struct Device *dev, uint8_t *in_data, size_t data_size, uint8_t command_ID); - int device_receive(struct Device *dev, uint8_t *out_data, size_t out_buffer_size); - int device_send_buf(struct Device *dev, uint8_t command_ID); -diff --git a/src/main.c b/src/main.c -index 059069e..9b38552 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -93,25 +93,46 @@ int parse_cmd_and_run(int argc, char *const *argv) { - res = RET_NO_ERROR; - break; - case 'i': {// id | info -- struct ResponseStatus status; -+ struct FullResponseStatus status; -+ memset(&status, 0, sizeof (struct FullResponseStatus)); -+ - res = device_get_status(&dev, &status); - check_ret((res != RET_NO_ERROR) && (res != RET_NO_PIN_ATTEMPTS), res); - if (strnlen(argv[1], 10) == 2 && argv[1][1] == 'd') { - // id command - print ID only -- print_card_serial(&status); -+ print_card_serial(&status.response_status); - } else { - // info command - print status - printf("Connected device status:\n"); - printf("\tCard serial: "); -- print_card_serial(&status); -- printf("\tFirmware: v%d.%d\n", -- status.firmware_version_st.major, -- status.firmware_version_st.minor); -- if (res != RET_NO_PIN_ATTEMPTS) { -- printf("\tCard counters: Admin %d, User %d\n", -- status.retry_admin, status.retry_user); -+ print_card_serial(&status.response_status); -+ if (status.device_type == Nk3) { -+ printf("\tFirmware Nitrokey 3: v%d.%d.%d\n", -+ (status.nk3_extra_info.firmware_version >> 22) & 0b1111111111, -+ (status.nk3_extra_info.firmware_version >> 6) & 0xFFFF, -+ status.nk3_extra_info.firmware_version & 0b111111); -+ printf("\tFirmware Secrets App: v%d.%d\n", -+ status.response_status.firmware_version_st.major, -+ status.response_status.firmware_version_st.minor); -+ if (res != RET_NO_PIN_ATTEMPTS) { -+ printf("\tSecrets app PIN counter: %d\n", -+ status.response_status.retry_user); -+ } else { -+ printf("\tSecrets app PIN counter: PIN is not set - set PIN before the first use\n"); -+ } -+ printf("\tGPG Card counters: Admin %d, User %d\n", -+ status.nk3_extra_info.pgp_admin_pin_retries, -+ status.nk3_extra_info.pgp_user_pin_retries); - } else { -- printf("\tCard counters: PIN is not set - set PIN before the first use\n"); -+ printf("\tFirmware: v%d.%d\n", -+ status.response_status.firmware_version_st.major, -+ status.response_status.firmware_version_st.minor); -+ if (res != RET_NO_PIN_ATTEMPTS) { -+ printf("\tCard counters: Admin %d, User %d\n", -+ status.response_status.retry_admin, status.response_status.retry_user); -+ } else { -+ printf("\tCard counters: PIN is not set - set PIN before the first use\n"); -+ } - } - } - if (res == RET_NO_PIN_ATTEMPTS) { -diff --git a/src/operations_ccid.c b/src/operations_ccid.c -index eb46124..25772e5 100644 ---- a/src/operations_ccid.c -+++ b/src/operations_ccid.c -@@ -273,14 +273,102 @@ int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify) { - return RET_VALIDATION_PASSED; - } - --int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number) { -+int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response) { -+ rassert(full_response != NULL); -+ struct ResponseStatus *response = &full_response->response_status; - rassert(handle != NULL); -- rassert(attempt_counter != NULL); -- rassert(firmware_version != NULL); -- rassert(serial_number != NULL); - uint8_t buf[1024] = {}; - IccResult iccResult = {}; -- int r = send_select_ccid(handle, buf, sizeof buf, &iccResult); -+ bool pin_counter_is_error = false; -+ int r; -+ libusb_device *usb_dev; -+ struct libusb_device_descriptor usb_desc; -+ -+ usb_dev = libusb_get_device(handle); -+ -+ r = libusb_get_device_descriptor(usb_dev, &usb_desc); -+ -+ if (r < 0) { -+ return r; -+ } -+ -+ -+ if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_3_USB_PID) { -+ full_response->device_type = Nk3; -+ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_PRO_USB_PID) { -+ full_response->device_type = NkPro2; -+ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_STORAGE_USB_PID) { -+ full_response->device_type = NkStorage; -+ } else if (usb_desc.idVendor == LIBREM_KEY_USB_VID || usb_desc.idProduct == LIBREM_KEY_USB_PID) { -+ full_response->device_type = LibremKey; -+ } -+ -+ if (full_response->device_type == Nk3) { -+ r = send_select_nk3_admin_ccid(handle, buf, sizeof buf, &iccResult); -+ if (r != RET_NO_ERROR) { -+ return r; -+ } -+ -+ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; -+ uint32_t iso_actual_length = iso7816_compose( -+ data_iso, sizeof data_iso, -+ 0x61, 0, 0, 0, 4, NULL, 0); -+ -+ // encode ccid wrapper -+ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, -+ 0x6F, iso_actual_length, -+ 0, 0, 0, data_iso); -+ int transferred; -+ r = ccid_send(handle, &transferred, buf, icc_actual_length); -+ if (r != 0) { -+ return r; -+ } -+ -+ r = ccid_receive(handle, &transferred, buf, sizeof buf); -+ if (r != 0) { -+ return r; -+ } -+ -+ IccResult iccResult = parse_icc_result(buf, transferred); -+ rassert(iccResult.data_status_code == 0x9000); -+ rassert(iccResult.data_len == 6); -+ full_response->nk3_extra_info.firmware_version = be32toh(*(uint32_t *) iccResult.data); -+ } -+ -+ if (full_response->device_type == Nk3) { -+ r = send_select_nk3_pgp_ccid(handle, buf, sizeof buf, &iccResult); -+ if (r != RET_NO_ERROR) { -+ return r; -+ } -+ -+ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; -+ uint32_t iso_actual_length = iso7816_compose( -+ data_iso, sizeof data_iso, -+ 0xCA, 0, 0xC4, 0, 0xFF, NULL, 0); -+ -+ // encode ccid wrapper -+ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, -+ 0x6F, iso_actual_length, -+ 0, 0, 0, data_iso); -+ int transferred; -+ r = ccid_send(handle, &transferred, buf, icc_actual_length); -+ if (r != 0) { -+ return r; -+ } -+ -+ r = ccid_receive(handle, &transferred, buf, sizeof buf); -+ if (r != 0) { -+ return r; -+ } -+ -+ IccResult iccResult = parse_icc_result(buf, transferred); -+ rassert(iccResult.data_status_code == 0x9000); -+ rassert(iccResult.data_len == 9); -+ full_response->nk3_extra_info.pgp_user_pin_retries = iccResult.data[4]; -+ full_response->nk3_extra_info.pgp_admin_pin_retries = iccResult.data[6]; -+ } -+ -+ r = send_select_ccid(handle, buf, sizeof buf, &iccResult); - if (r != RET_NO_ERROR) { - return r; - } -@@ -292,29 +380,30 @@ int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *fi - r = get_tlv(iccResult.data, iccResult.data_len, Tag_PINCounter, &counter_tlv); - if (!(r == RET_NO_ERROR && counter_tlv.tag == Tag_PINCounter)) { - // PIN counter not found - comm error (ignore) or PIN not set -- *attempt_counter = -1; -+ pin_counter_is_error = true; - } else { -- *attempt_counter = counter_tlv.v_data[0]; -+ response->retry_admin = counter_tlv.v_data[0]; -+ response->retry_user = counter_tlv.v_data[0]; - } - - TLV serial_tlv = {}; - r = get_tlv(iccResult.data, iccResult.data_len, Tag_SerialNumber, &serial_tlv); - if (r == RET_NO_ERROR && serial_tlv.tag == Tag_SerialNumber) { -- *serial_number = be32toh(*(uint32_t *) serial_tlv.v_data); -+ response->card_serial_u32 = be32toh(*(uint32_t *) serial_tlv.v_data); - } else { - // ignore errors - unsupported or hidden serial_tlv number -- *serial_number = 0; -+ response->card_serial_u32 = 0; - } - - TLV version_tlv = {}; - r = get_tlv(iccResult.data, iccResult.data_len, Tag_Version, &version_tlv); - if (!(r == RET_NO_ERROR && version_tlv.tag == Tag_Version)) { -- *firmware_version = 0; -+ response->firmware_version = 0; - return RET_COMM_ERROR; - } -- *firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); -+ response->firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); - -- if (*attempt_counter == -1) { -+ if (pin_counter_is_error == true) { - return RET_NO_PIN_ATTEMPTS; - } - return RET_NO_ERROR; -diff --git a/src/operations_ccid.h b/src/operations_ccid.h -index b26b3c7..ea463b4 100644 ---- a/src/operations_ccid.h -+++ b/src/operations_ccid.h -@@ -10,7 +10,7 @@ int authenticate_ccid(struct Device *dev, const char *admin_PIN); - int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); - int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); - int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); --int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); -+int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response); - - - #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H -diff --git a/src/structs.h b/src/structs.h -index 6309cd0..9e87134 100644 ---- a/src/structs.h -+++ b/src/structs.h -@@ -116,6 +116,25 @@ struct ResponseStatus { - uint8_t retry_user; /*not present in the firmware response for the Status command in v0.8 firmware*/ - }; - -+enum DeviceType { -+ Unknown = 0, -+ Nk3, -+ NkPro2, -+ NkStorage, -+ LibremKey, -+}; -+ -+struct FullResponseStatus { -+ enum DeviceType device_type; -+ struct ResponseStatus response_status; -+ struct { -+ // Only valid if device_type is NK3 -+ uint8_t pgp_admin_pin_retries; -+ uint8_t pgp_user_pin_retries; -+ uint32_t firmware_version; -+ } nk3_extra_info; -+}; -+ - - struct WriteToOTPSlot { - //admin auth From b760e636fd76513dacf8f1db7fa34ce0b8ee9777 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 13:42:11 -0500 Subject: [PATCH 12/25] oem-factory-reset: don't set user re-ownership by default for now: use current defaults being DEF pins (12345678 and 123456 as master) Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 3 --- 1 file changed, 3 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 7c0340aa7..e69a7ddd1 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -86,9 +86,6 @@ done # Handle the --mode parameter if provided if [[ -n "$MODE" ]]; then handle_mode "$MODE" -else - # Default to User Re-Ownership mode - handle_mode "user" fi #Override RSA_KEY_LENGTH to 2048 bits for Canokey under qemu testing boards until canokey fixes From 85dfaf9ac2d0b11bec8114ba1da42f8a6597c036 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 13:46:25 -0500 Subject: [PATCH 13/25] oem-factory-reset: if nk3, also display Secure App PIN = GPG Admin PIN as text and in Qr code Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index e69a7ddd1..74894e875 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -1387,6 +1387,12 @@ fi #GPG PINs output passphrases+="GPG Admin PIN: ${ADMIN_PIN}\n" + +#if nk3 detected, we add the NK3 Secre App PIN. Detect by product ID +if lsusb | grep -q "20a0:42b2"; then + passphrases+="Nitrokey 3 Security App PIN: ${ADMIN_PIN}\n" +fi + #USER PIN was configured if GPG_GEN_KEY_IN_MEMORY is not active or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is active if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then passphrases+="GPG User PIN: ${USER_PIN}\n" @@ -1397,6 +1403,7 @@ if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n" fi + # Show configured secrets in whiptail and loop until user confirms qr code was scanned while true; do whiptail --msgbox " From 91704d0c0a1f09ec2f70389b742d8fb96c1b3866 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 13:55:39 -0500 Subject: [PATCH 14/25] oem-factory-reset: fix Secure App wording, prevent word globbing, warn that physical presence is needed Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 74894e875..3c99eb627 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -139,14 +139,15 @@ mount_boot() { reset_nk3_secret_app() { TRACE_FUNC - # Reset Nitrokey 3 secret app + # Reset Nitrokey 3 Secret App if lsusb | grep -q "20a0:42b2"; then echo - echo "Resetting Nitrokey 3 secret app" + echo "Resetting Nitrokey 3 Secret App PIN. Physical presence (touch) will be required" + #TODO, change message when https://github.com/Nitrokey/nitrokey-hotp-verification/issues/41 is fixed DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" killall -9 scdaemon 2>&1 >/dev/null || true - # Reset Nitrokey 3 secret app - /bin/hotp_verification reset $ADMIN_PIN + # Reset Nitrokey 3 secret app with PIN + /bin/hotp_verification reset "${ADMIN_PIN}" fi } @@ -548,7 +549,6 @@ gpg_key_factory_reset() { #Reset Nitrokey 3 secret app reset_nk3_secret_app - # Nk3 now ready to set secret app PIN on first use... # If Nitrokey Storage is inserted, reset AES keys as well if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then @@ -1390,7 +1390,7 @@ passphrases+="GPG Admin PIN: ${ADMIN_PIN}\n" #if nk3 detected, we add the NK3 Secre App PIN. Detect by product ID if lsusb | grep -q "20a0:42b2"; then - passphrases+="Nitrokey 3 Security App PIN: ${ADMIN_PIN}\n" + passphrases+="Nitrokey 3 Secret App PIN: ${ADMIN_PIN}\n" fi #USER PIN was configured if GPG_GEN_KEY_IN_MEMORY is not active or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is active From e43626016a57ab68b3908f0c94ba70d5c4bb709c Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 14:25:22 -0500 Subject: [PATCH 15/25] oem-factory-reset: set title_text accordingly to mode, either 'OEM Factory Reset Mode', 'Re-Ownership Mode' or 'OEM Factory Reset / Re-Ownership' TODO: further specialize warning prompt to tell what is going to happen (randomized PIN, signle custom randomized PIN etc) Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 3c99eb627..939377102 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -52,12 +52,16 @@ handle_mode() { USER_PIN=$CUSTOM_SINGLE_PASS ADMIN_PIN=$CUSTOM_SINGLE_PASS TPM_PASS=$CUSTOM_SINGLE_PASS + + title_text="OEM Factory Reset Mode" ;; user) DEBUG "User mode selected" USER_PIN=$(generate_passphrase --number_words 2 --max_length $MAX_HOTP_GPG_PIN_LENGTH) ADMIN_PIN=$(generate_passphrase --number_words 2 --max_length $MAX_HOTP_GPG_PIN_LENGTH) TPM_PASS=$ADMIN_PIN + + title_text="User Re-Ownership Mode" ;; *) warn "Unknown oem-factory-reset lauched mode, setting PINs to weak defaults" @@ -911,9 +915,7 @@ usb_security_token_capabilities_check() { ## main script start # check for args -if [ "$1" != "" ]; then - title_text=$1 -else +if [ -z "$title_text" ]; then title_text="OEM Factory Reset / Re-Ownership" fi if [ "$2" != "" ]; then From 444ff3ee3719956744f780e558bb3d72890e1224 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 14:37:48 -0500 Subject: [PATCH 16/25] oem-factory-reset: reset nk3 secure app PIN early since we need physical presence, put nk3 secure APP PIN after TPM but before GPG PINS in output for consistency Signed-off-by: Thierry Laurion --- initrd/bin/oem-factory-reset | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 939377102..a3f6933f3 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -148,8 +148,6 @@ reset_nk3_secret_app() { echo echo "Resetting Nitrokey 3 Secret App PIN. Physical presence (touch) will be required" #TODO, change message when https://github.com/Nitrokey/nitrokey-hotp-verification/issues/41 is fixed - DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" - killall -9 scdaemon 2>&1 >/dev/null || true # Reset Nitrokey 3 secret app with PIN /bin/hotp_verification reset "${ADMIN_PIN}" fi @@ -537,6 +535,9 @@ gpg_key_factory_reset() { #enable usb storage enable_usb + #Reset Nitrokey 3 secret app + reset_nk3_secret_app + # Factory reset GPG card echo "GPG factory reset of USB Security Dongle's smartcard..." { @@ -551,8 +552,6 @@ gpg_key_factory_reset() { whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR" fi - #Reset Nitrokey 3 secret app - reset_nk3_secret_app # If Nitrokey Storage is inserted, reset AES keys as well if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then @@ -1387,14 +1386,13 @@ if [ "$CONFIG_TPM" = "y" ]; then passphrases+="TPM Owner Password: ${TPM_PASS}\n" fi -#GPG PINs output -passphrases+="GPG Admin PIN: ${ADMIN_PIN}\n" - #if nk3 detected, we add the NK3 Secre App PIN. Detect by product ID if lsusb | grep -q "20a0:42b2"; then passphrases+="Nitrokey 3 Secret App PIN: ${ADMIN_PIN}\n" fi +#GPG PINs output +passphrases+="GPG Admin PIN: ${ADMIN_PIN}\n" #USER PIN was configured if GPG_GEN_KEY_IN_MEMORY is not active or if GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD is active if [ "$GPG_GEN_KEY_IN_MEMORY" = "n" -o "$GPG_GEN_KEY_IN_MEMORY_COPY_TO_SMARTCARD" = "y" ]; then passphrases+="GPG User PIN: ${USER_PIN}\n" From 835b7acfcb3e8bd9446eac303a50e46962e92d05 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 16:08:34 -0500 Subject: [PATCH 17/25] kexec-sign-config: mount rw, write things to /boot, mount ro after Signed-off-by: Thierry Laurion --- initrd/bin/kexec-sign-config | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/initrd/bin/kexec-sign-config b/initrd/bin/kexec-sign-config index c34060d0b..d0d66c69e 100755 --- a/initrd/bin/kexec-sign-config +++ b/initrd/bin/kexec-sign-config @@ -27,6 +27,9 @@ assert_signable confirm_gpg_card +# remount /boot as rw +mount -o remount,rw /boot + # update hashes in /boot before signing if [ "$update" = "y" ]; then ( @@ -81,8 +84,15 @@ for tries in 1 2 3; do ; then # successful - update the validated params check_config $paramsdir + + # remount /boot as ro + mount -o remount,ro /boot + exit 0 fi done +# remount /boot as ro +mount -o remount,ro /boot + die "$paramsdir: Unable to sign kexec hashes" From 295935f311680d73d760776c8dc3e92da4dce06d Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 5 Dec 2024 16:48:32 -0500 Subject: [PATCH 18/25] WiP seal-hotp: customize message to be GPG Admin PIN or Secure App PIN TODO: check logic in this file because assumptions on PINs retry count are wrong and will depend on https://github.com/Nitrokey/nitrokey-hotp-verification/pull/43 not tested here Signed-off-by: Thierry Laurion --- initrd/bin/seal-hotpkey | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/initrd/bin/seal-hotpkey b/initrd/bin/seal-hotpkey index 3f91edcc9..7e6e839f9 100755 --- a/initrd/bin/seal-hotpkey +++ b/initrd/bin/seal-hotpkey @@ -126,23 +126,33 @@ else fi if [ "$admin_pin_status" -ne 0 ]; then + + # create custom message for PIN prompt based on nk3 lsusb product id + prompt_message="" + if lsusb | grep -q "20a0:42b2"; then + prompt_message="Secure App" + else + prompt_message="GPG Admin" + fi + + # prompt user for PIN and retry echo "" - read -s -p "Enter your $HOTPKEY_BRANDING Admin PIN: " admin_pin + read -s -p "Enter your $HOTPKEY_BRANDING $prompt_message PIN: " admin_pin echo -e "\n" hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" if [ $? -ne 0 ]; then echo -e "\n" - read -s -p "Error setting HOTP secret, re-enter Admin PIN and try again: " admin_pin + read -s -p "Error setting HOTP secret, re-enter $prompt_message PIN and try again: " admin_pin echo -e "\n" if ! hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" ; then # don't leak key on failure shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null if [ "$HOTPKEY_BRANDING" == "Nitrokey" ]; then - fatal_error "Setting HOTP secret failed, to reset nitrokey pin use: nitropy nk3 secrets reset or the Nitrokey App 2" + fatal_error "Setting HOTP secret failed, to reset $prompt_message PIN, redo Re-Ownership procedure, the Nitrokey App 2 or contact Nitrokey support" else - fatal_error "Setting HOTP secret failed" + fatal_error "Setting HOTP secret failed" fi fi fi From e73bb055579889b38ab5b980b7f2364207a8f032 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 6 Dec 2024 10:50:59 -0500 Subject: [PATCH 19/25] hotp-verification patches: Use https://github.com/Nitrokey/nitrokey-hotp-verification/pull/43 instead of https://github.com/Nitrokey/nitrokey-hotp-verification/pull/46 for hotp-verification info parsing and validation of oem-factory-reset and seal-hotp Signed-off-by: Thierry Laurion --- .../43.patch | 450 ++++++++++++++++++ .../46.patch | 219 --------- 2 files changed, 450 insertions(+), 219 deletions(-) create mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch delete mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch new file mode 100644 index 000000000..26ad2bc45 --- /dev/null +++ b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch @@ -0,0 +1,450 @@ +From 707c6545a509eeb24a06537e5f835d786c2e657e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Thu, 5 Dec 2024 16:44:30 +0100 +Subject: [PATCH] Add support for nitrokey 3 distinction between the secrets + app and other + +This now adds the secrets app version and the nitrokey 3 firmware version, +and also the gpg pins +--- + src/ccid.c | 71 +++++++++++++++++++++++++- + src/ccid.h | 2 + + src/device.c | 29 +++++------ + src/device.h | 2 +- + src/main.c | 41 +++++++++++---- + src/operations_ccid.c | 113 +++++++++++++++++++++++++++++++++++++----- + src/operations_ccid.h | 2 +- + src/structs.h | 19 +++++++ + 8 files changed, 238 insertions(+), 41 deletions(-) + +diff --git a/src/ccid.c b/src/ccid.c +index 9cf24a0..a2cc919 100644 +--- a/src/ccid.c ++++ b/src/ccid.c +@@ -104,7 +104,7 @@ IccResult parse_icc_result(uint8_t *buf, size_t buf_len) { + // .buffer_len = buf_len + }; + // Make sure the response do not contain overread attempts +- rassert(i.data_len < buf_len - 10); ++ rassert(i.data_len <= buf_len - 10); + return i; + } + +@@ -307,6 +307,75 @@ int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_siz + return RET_NO_ERROR; + } + ++int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { ++ unsigned char cmd_select[] = { ++ 0x6f, ++ 0x0E, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0xa4, ++ 0x04, ++ 0x00, ++ 0x09, ++ 0xa0, ++ 0x00, ++ 0x00, ++ 0x08, ++ 0x47, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x01, ++ }; ++ ++ check_ret( ++ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), ++ RET_COMM_ERROR); ++ ++ ++ return RET_NO_ERROR; ++} ++ ++int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { ++ unsigned char cmd_select[] = { ++ 0x6f, ++ 0x0C, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0x00, ++ 0xA4, ++ 0x04, ++ 0x00, ++ 0x06, ++ 0xD2, ++ 0x76, ++ 0x00, ++ 0x01, ++ 0x24, ++ 0x01, ++ 0x00, ++ }; ++ ++ check_ret( ++ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), ++ RET_COMM_ERROR); ++ ++ ++ return RET_NO_ERROR; ++} + + int ccid_init(libusb_device_handle *handle) { + +diff --git a/src/ccid.h b/src/ccid.h +index ed17dc7..3dcf106 100644 +--- a/src/ccid.h ++++ b/src/ccid.h +@@ -70,6 +70,8 @@ uint32_t icc_pack_tlvs_for_sending(uint8_t *buf, size_t buflen, TLV tlvs[], int + libusb_device_handle *get_device(libusb_context *ctx, const struct VidPid pPid[], int devices_count); + int ccid_init(libusb_device_handle *handle); + int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); ++int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); ++int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); + + + enum { +diff --git a/src/device.c b/src/device.c +index 4b9361e..52acece 100644 +--- a/src/device.c ++++ b/src/device.c +@@ -29,6 +29,7 @@ + #include "structs.h" + #include "utils.h" + #include ++#include + #include + #include + #include +@@ -259,23 +260,19 @@ int device_receive_buf(struct Device *dev) { + + #include "operations_ccid.h" + +-int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { +- assert(out_status != NULL); ++int device_get_status(struct Device *dev, struct FullResponseStatus *out_response) { ++ assert(out_response != NULL); + assert(dev != NULL); +- memset(out_status, 0, sizeof(struct ResponseStatus)); ++ memset(out_response, 0, sizeof(struct FullResponseStatus)); ++ ++ struct ResponseStatus *out_status = &out_response->response_status; + + if (dev->connection_type == CONNECTION_CCID) { +- int counter = 0; +- uint32_t serial = 0; +- uint16_t version = 0; +- int res = status_ccid(dev->mp_devhandle_ccid, +- &counter, +- &version, +- &serial); +- out_status->retry_admin = counter; +- out_status->retry_user = counter; +- out_status->card_serial_u32 = serial; +- out_status->firmware_version = version; ++ int res = status_ccid(dev->mp_devhandle_ccid, out_response); ++ // out_status->retry_admin = counter; ++ // out_status->retry_user = counter; ++ // out_status->card_serial_u32 = serial; ++ // out_status->firmware_version = version; + return res; + } + +@@ -290,7 +287,7 @@ int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { + + device_send_buf(dev, GET_STATUS); + device_receive_buf(dev); +- *out_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; ++ out_response->response_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; + + if (out_status->firmware_version_st.minor == 1) { + for (int i = 0; i < 100; ++i) { +@@ -343,4 +340,4 @@ const char *command_status_to_string(uint8_t status_code) { + void clean_buffers(struct Device *dev) { + memset(dev->ccid_buffer_in, 0, sizeof dev->ccid_buffer_in); + memset(dev->ccid_buffer_out, 0, sizeof dev->ccid_buffer_out); +-} +\ No newline at end of file ++} +diff --git a/src/device.h b/src/device.h +index c895546..97feeeb 100644 +--- a/src/device.h ++++ b/src/device.h +@@ -72,7 +72,7 @@ struct Device { + + int device_connect(struct Device *dev); + int device_disconnect(struct Device *dev); +-int device_get_status(struct Device *dev, struct ResponseStatus *out_status); ++int device_get_status(struct Device *dev, struct FullResponseStatus *out_status); + int device_send(struct Device *dev, uint8_t *in_data, size_t data_size, uint8_t command_ID); + int device_receive(struct Device *dev, uint8_t *out_data, size_t out_buffer_size); + int device_send_buf(struct Device *dev, uint8_t command_ID); +diff --git a/src/main.c b/src/main.c +index 059069e..9b38552 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -93,25 +93,46 @@ int parse_cmd_and_run(int argc, char *const *argv) { + res = RET_NO_ERROR; + break; + case 'i': {// id | info +- struct ResponseStatus status; ++ struct FullResponseStatus status; ++ memset(&status, 0, sizeof (struct FullResponseStatus)); ++ + res = device_get_status(&dev, &status); + check_ret((res != RET_NO_ERROR) && (res != RET_NO_PIN_ATTEMPTS), res); + if (strnlen(argv[1], 10) == 2 && argv[1][1] == 'd') { + // id command - print ID only +- print_card_serial(&status); ++ print_card_serial(&status.response_status); + } else { + // info command - print status + printf("Connected device status:\n"); + printf("\tCard serial: "); +- print_card_serial(&status); +- printf("\tFirmware: v%d.%d\n", +- status.firmware_version_st.major, +- status.firmware_version_st.minor); +- if (res != RET_NO_PIN_ATTEMPTS) { +- printf("\tCard counters: Admin %d, User %d\n", +- status.retry_admin, status.retry_user); ++ print_card_serial(&status.response_status); ++ if (status.device_type == Nk3) { ++ printf("\tFirmware Nitrokey 3: v%d.%d.%d\n", ++ (status.nk3_extra_info.firmware_version >> 22) & 0b1111111111, ++ (status.nk3_extra_info.firmware_version >> 6) & 0xFFFF, ++ status.nk3_extra_info.firmware_version & 0b111111); ++ printf("\tFirmware Secrets App: v%d.%d\n", ++ status.response_status.firmware_version_st.major, ++ status.response_status.firmware_version_st.minor); ++ if (res != RET_NO_PIN_ATTEMPTS) { ++ printf("\tSecrets app PIN counter: %d\n", ++ status.response_status.retry_user); ++ } else { ++ printf("\tSecrets app PIN counter: PIN is not set - set PIN before the first use\n"); ++ } ++ printf("\tGPG Card counters: Admin %d, User %d\n", ++ status.nk3_extra_info.pgp_admin_pin_retries, ++ status.nk3_extra_info.pgp_user_pin_retries); + } else { +- printf("\tCard counters: PIN is not set - set PIN before the first use\n"); ++ printf("\tFirmware: v%d.%d\n", ++ status.response_status.firmware_version_st.major, ++ status.response_status.firmware_version_st.minor); ++ if (res != RET_NO_PIN_ATTEMPTS) { ++ printf("\tCard counters: Admin %d, User %d\n", ++ status.response_status.retry_admin, status.response_status.retry_user); ++ } else { ++ printf("\tCard counters: PIN is not set - set PIN before the first use\n"); ++ } + } + } + if (res == RET_NO_PIN_ATTEMPTS) { +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index eb46124..25772e5 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -273,14 +273,102 @@ int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify) { + return RET_VALIDATION_PASSED; + } + +-int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number) { ++int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response) { ++ rassert(full_response != NULL); ++ struct ResponseStatus *response = &full_response->response_status; + rassert(handle != NULL); +- rassert(attempt_counter != NULL); +- rassert(firmware_version != NULL); +- rassert(serial_number != NULL); + uint8_t buf[1024] = {}; + IccResult iccResult = {}; +- int r = send_select_ccid(handle, buf, sizeof buf, &iccResult); ++ bool pin_counter_is_error = false; ++ int r; ++ libusb_device *usb_dev; ++ struct libusb_device_descriptor usb_desc; ++ ++ usb_dev = libusb_get_device(handle); ++ ++ r = libusb_get_device_descriptor(usb_dev, &usb_desc); ++ ++ if (r < 0) { ++ return r; ++ } ++ ++ ++ if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_3_USB_PID) { ++ full_response->device_type = Nk3; ++ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_PRO_USB_PID) { ++ full_response->device_type = NkPro2; ++ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_STORAGE_USB_PID) { ++ full_response->device_type = NkStorage; ++ } else if (usb_desc.idVendor == LIBREM_KEY_USB_VID || usb_desc.idProduct == LIBREM_KEY_USB_PID) { ++ full_response->device_type = LibremKey; ++ } ++ ++ if (full_response->device_type == Nk3) { ++ r = send_select_nk3_admin_ccid(handle, buf, sizeof buf, &iccResult); ++ if (r != RET_NO_ERROR) { ++ return r; ++ } ++ ++ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; ++ uint32_t iso_actual_length = iso7816_compose( ++ data_iso, sizeof data_iso, ++ 0x61, 0, 0, 0, 4, NULL, 0); ++ ++ // encode ccid wrapper ++ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, ++ 0x6F, iso_actual_length, ++ 0, 0, 0, data_iso); ++ int transferred; ++ r = ccid_send(handle, &transferred, buf, icc_actual_length); ++ if (r != 0) { ++ return r; ++ } ++ ++ r = ccid_receive(handle, &transferred, buf, sizeof buf); ++ if (r != 0) { ++ return r; ++ } ++ ++ IccResult iccResult = parse_icc_result(buf, transferred); ++ rassert(iccResult.data_status_code == 0x9000); ++ rassert(iccResult.data_len == 6); ++ full_response->nk3_extra_info.firmware_version = be32toh(*(uint32_t *) iccResult.data); ++ } ++ ++ if (full_response->device_type == Nk3) { ++ r = send_select_nk3_pgp_ccid(handle, buf, sizeof buf, &iccResult); ++ if (r != RET_NO_ERROR) { ++ return r; ++ } ++ ++ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; ++ uint32_t iso_actual_length = iso7816_compose( ++ data_iso, sizeof data_iso, ++ 0xCA, 0, 0xC4, 0, 0xFF, NULL, 0); ++ ++ // encode ccid wrapper ++ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, ++ 0x6F, iso_actual_length, ++ 0, 0, 0, data_iso); ++ int transferred; ++ r = ccid_send(handle, &transferred, buf, icc_actual_length); ++ if (r != 0) { ++ return r; ++ } ++ ++ r = ccid_receive(handle, &transferred, buf, sizeof buf); ++ if (r != 0) { ++ return r; ++ } ++ ++ IccResult iccResult = parse_icc_result(buf, transferred); ++ rassert(iccResult.data_status_code == 0x9000); ++ rassert(iccResult.data_len == 9); ++ full_response->nk3_extra_info.pgp_user_pin_retries = iccResult.data[4]; ++ full_response->nk3_extra_info.pgp_admin_pin_retries = iccResult.data[6]; ++ } ++ ++ r = send_select_ccid(handle, buf, sizeof buf, &iccResult); + if (r != RET_NO_ERROR) { + return r; + } +@@ -292,29 +380,30 @@ int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *fi + r = get_tlv(iccResult.data, iccResult.data_len, Tag_PINCounter, &counter_tlv); + if (!(r == RET_NO_ERROR && counter_tlv.tag == Tag_PINCounter)) { + // PIN counter not found - comm error (ignore) or PIN not set +- *attempt_counter = -1; ++ pin_counter_is_error = true; + } else { +- *attempt_counter = counter_tlv.v_data[0]; ++ response->retry_admin = counter_tlv.v_data[0]; ++ response->retry_user = counter_tlv.v_data[0]; + } + + TLV serial_tlv = {}; + r = get_tlv(iccResult.data, iccResult.data_len, Tag_SerialNumber, &serial_tlv); + if (r == RET_NO_ERROR && serial_tlv.tag == Tag_SerialNumber) { +- *serial_number = be32toh(*(uint32_t *) serial_tlv.v_data); ++ response->card_serial_u32 = be32toh(*(uint32_t *) serial_tlv.v_data); + } else { + // ignore errors - unsupported or hidden serial_tlv number +- *serial_number = 0; ++ response->card_serial_u32 = 0; + } + + TLV version_tlv = {}; + r = get_tlv(iccResult.data, iccResult.data_len, Tag_Version, &version_tlv); + if (!(r == RET_NO_ERROR && version_tlv.tag == Tag_Version)) { +- *firmware_version = 0; ++ response->firmware_version = 0; + return RET_COMM_ERROR; + } +- *firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); ++ response->firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); + +- if (*attempt_counter == -1) { ++ if (pin_counter_is_error == true) { + return RET_NO_PIN_ATTEMPTS; + } + return RET_NO_ERROR; +diff --git a/src/operations_ccid.h b/src/operations_ccid.h +index b26b3c7..ea463b4 100644 +--- a/src/operations_ccid.h ++++ b/src/operations_ccid.h +@@ -10,7 +10,7 @@ int authenticate_ccid(struct Device *dev, const char *admin_PIN); + int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); + int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); + int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); +-int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); ++int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response); + + + #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H +diff --git a/src/structs.h b/src/structs.h +index 6309cd0..9e87134 100644 +--- a/src/structs.h ++++ b/src/structs.h +@@ -116,6 +116,25 @@ struct ResponseStatus { + uint8_t retry_user; /*not present in the firmware response for the Status command in v0.8 firmware*/ + }; + ++enum DeviceType { ++ Unknown = 0, ++ Nk3, ++ NkPro2, ++ NkStorage, ++ LibremKey, ++}; ++ ++struct FullResponseStatus { ++ enum DeviceType device_type; ++ struct ResponseStatus response_status; ++ struct { ++ // Only valid if device_type is NK3 ++ uint8_t pgp_admin_pin_retries; ++ uint8_t pgp_user_pin_retries; ++ uint32_t firmware_version; ++ } nk3_extra_info; ++}; ++ + + struct WriteToOTPSlot { + //admin auth diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch deleted file mode 100644 index 781c10ffa..000000000 --- a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch +++ /dev/null @@ -1,219 +0,0 @@ -From de355ed93ba50280bf377772082b76b7a2285185 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= -Date: Mon, 25 Nov 2024 17:04:47 +0100 -Subject: [PATCH 1/3] Add reset command for nitrokey 3 - ---- - src/main.c | 10 ++++++++-- - src/operations_ccid.c | 41 +++++++++++++++++++++++++++++++++++++++++ - src/operations_ccid.h | 1 + - 3 files changed, 50 insertions(+), 2 deletions(-) - -diff --git a/src/main.c b/src/main.c -index 059069e..b80b71d 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -21,6 +21,7 @@ - - #include "ccid.h" - #include "operations.h" -+#include "operations_ccid.h" - #include "return_codes.h" - #include "utils.h" - #include "version.h" -@@ -134,8 +135,13 @@ int parse_cmd_and_run(int argc, char *const *argv) { - } - break; - case 'r': -- if (argc != 3) break; -- res = regenerate_AES_key(&dev, argv[2]); -+ if (strncmp(argv[1], "reset", 15) == 0) { -+ if (argc != 2) break; -+ res = nk3_reset(&dev); -+ } else if (strncmp(argv[1], "regenerate", 15) == 0) { -+ if (argc != 3) break; -+ res = regenerate_AES_key(&dev, argv[2]); -+ } - break; - default: - break; -diff --git a/src/operations_ccid.c b/src/operations_ccid.c -index eb46124..574155d 100644 ---- a/src/operations_ccid.c -+++ b/src/operations_ccid.c -@@ -32,6 +32,47 @@ - #include - - -+ -+int nk3_reset(struct Device *dev) { -+ libusb_device *usb_dev; -+ struct libusb_device_descriptor usb_desc; -+ usb_dev = libusb_get_device(dev->mp_devhandle_ccid); -+ -+ int r = libusb_get_device_descriptor(usb_dev, &usb_desc); -+ -+ if (r < 0) { -+ return r; -+ } -+ -+ -+ if (usb_desc.idVendor != NITROKEY_USB_VID || usb_desc.idProduct != NITROKEY_3_USB_PID) { -+ return 0; -+ } -+ -+ -+ uint8_t buf[10]; -+ // encode -+ uint32_t icc_actual_length = iso7816_compose(buf, sizeof buf, Ins_Reset, 0xDE, 0xAD, 0, 0, NULL, 0); -+ -+ // encode ccid wrapper -+ icc_actual_length = icc_compose(dev->ccid_buffer_out, sizeof dev->ccid_buffer_out, -+ 0x6F, icc_actual_length, -+ 0, 0, 0, buf); -+ // send -+ IccResult iccResult; -+ r = ccid_process_single(dev->mp_devhandle_ccid, dev->ccid_buffer_in, sizeof dev->ccid_buffer_in, -+ dev->ccid_buffer_out, icc_actual_length, &iccResult); -+ if (r != 0) { -+ return r; -+ } -+ // check status code -+ if (iccResult.data_status_code != 0x9000) { -+ return 1; -+ } -+ -+ return RET_NO_ERROR; -+} -+ - int set_pin_ccid(struct Device *dev, const char *admin_PIN) { - TLV tlvs[] = { - { -diff --git a/src/operations_ccid.h b/src/operations_ccid.h -index b26b3c7..ec0070c 100644 ---- a/src/operations_ccid.h -+++ b/src/operations_ccid.h -@@ -11,6 +11,7 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); - int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); - int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); - int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); -+int nk3_reset(struct Device *dev); - - - #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H - -From 8425e8c622138aef9ab207119e14f7cbedd40175 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= -Date: Mon, 2 Dec 2024 10:29:59 +0100 -Subject: [PATCH 2/3] Add optional new pin when resetting - ---- - src/main.c | 9 +++++---- - src/operations_ccid.c | 6 +++++- - src/operations_ccid.h | 5 ++++- - 3 files changed, 14 insertions(+), 6 deletions(-) - -diff --git a/src/main.c b/src/main.c -index b80b71d..3f4a1cc 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -38,9 +38,10 @@ void print_help(char *app_name) { - "\t%s info\n" - "\t%s version\n" - "\t%s check \n" -- "\t%s regenerate \n" -+ "\t%s reset [ADMIN PIN]\n" -+ "\t%s regenerate\n" - "\t%s set [COUNTER]\n", -- app_name, app_name, app_name, app_name, app_name, app_name); -+ app_name, app_name, app_name, app_name, app_name, app_name, app_name); - } - - -@@ -136,8 +137,8 @@ int parse_cmd_and_run(int argc, char *const *argv) { - break; - case 'r': - if (strncmp(argv[1], "reset", 15) == 0) { -- if (argc != 2) break; -- res = nk3_reset(&dev); -+ if (argc != 2 && argc != 3) break; -+ res = nk3_reset(&dev, argc == 3 ? argv[2]: NULL); - } else if (strncmp(argv[1], "regenerate", 15) == 0) { - if (argc != 3) break; - res = regenerate_AES_key(&dev, argv[2]); -diff --git a/src/operations_ccid.c b/src/operations_ccid.c -index 574155d..07834ce 100644 ---- a/src/operations_ccid.c -+++ b/src/operations_ccid.c -@@ -33,7 +33,7 @@ - - - --int nk3_reset(struct Device *dev) { -+int nk3_reset(struct Device *dev, const char * new_pin) { - libusb_device *usb_dev; - struct libusb_device_descriptor usb_desc; - usb_dev = libusb_get_device(dev->mp_devhandle_ccid); -@@ -70,6 +70,10 @@ int nk3_reset(struct Device *dev) { - return 1; - } - -+ if (new_pin != NULL) { -+ set_pin_ccid(dev, new_pin); -+ } -+ - return RET_NO_ERROR; - } - -diff --git a/src/operations_ccid.h b/src/operations_ccid.h -index ec0070c..61cad72 100644 ---- a/src/operations_ccid.h -+++ b/src/operations_ccid.h -@@ -11,7 +11,10 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); - int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); - int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); - int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); --int nk3_reset(struct Device *dev); -+// new_pin can be `null` -+// -+// If it is, no new pin will be set -+int nk3_reset(struct Device *dev, const char * new_pin); - - - #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H - -From 596f701985682adf6bfab06c78cbe132cbcb2aae Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= -Date: Tue, 3 Dec 2024 10:48:27 +0100 -Subject: [PATCH 3/3] Fix null pointer bug on non nk3 - ---- - src/operations_ccid.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/src/operations_ccid.c b/src/operations_ccid.c -index 07834ce..538d434 100644 ---- a/src/operations_ccid.c -+++ b/src/operations_ccid.c -@@ -36,6 +36,12 @@ - int nk3_reset(struct Device *dev, const char * new_pin) { - libusb_device *usb_dev; - struct libusb_device_descriptor usb_desc; -+ -+ if (!dev->mp_devhandle_ccid) { -+ // Not an NK3 -+ return RET_NO_ERROR; -+ } -+ - usb_dev = libusb_get_device(dev->mp_devhandle_ccid); - - int r = libusb_get_device_descriptor(usb_dev, &usb_desc); -@@ -46,7 +52,7 @@ int nk3_reset(struct Device *dev, const char * new_pin) { - - - if (usb_desc.idVendor != NITROKEY_USB_VID || usb_desc.idProduct != NITROKEY_3_USB_PID) { -- return 0; -+ return RET_NO_ERROR; - } - - From d142f76202257e7a3fd718283ab61c080dfed233 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 6 Dec 2024 09:48:28 -0500 Subject: [PATCH 20/25] oem-factory-reset+seal-hotp nk3 hotp-verification info adaptations - oem-factory-reset: fix strings for nk3 is from https://github.com/Nitrokey/nitrokey-hotp-verification/pull/43 is Secrets app, not Secret App singular, not App capitalized - initrd/bin/seal-hotpkey: adapt to check nk3 Secrets App PIN counter if nk3, keep Card counters for --- initrd/bin/oem-factory-reset | 16 ++-- initrd/bin/seal-hotpkey | 151 +++++++++++++++++------------------ 2 files changed, 83 insertions(+), 84 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index a3f6933f3..613569530 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -143,13 +143,15 @@ mount_boot() { reset_nk3_secret_app() { TRACE_FUNC - # Reset Nitrokey 3 Secret App + # Reset Nitrokey 3 Secrets App if lsusb | grep -q "20a0:42b2"; then echo - echo "Resetting Nitrokey 3 Secret App PIN. Physical presence (touch) will be required" + warn "Resetting Nitrokey 3 Secrets App PIN. Physical presence (touch) will be required" #TODO, change message when https://github.com/Nitrokey/nitrokey-hotp-verification/issues/41 is fixed # Reset Nitrokey 3 secret app with PIN - /bin/hotp_verification reset "${ADMIN_PIN}" + if ! /bin/hotp_verification reset "${ADMIN_PIN}"; then + whiptail_error_die "Failed to reset Nitrokey 3 Secrets App with error code $?, contact Nitrokey support" + fi fi } @@ -552,7 +554,6 @@ gpg_key_factory_reset() { whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR" fi - # If Nitrokey Storage is inserted, reset AES keys as well if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ]; then DEBUG "Nitrokey Storage detected, resetting AES keys..." @@ -560,7 +561,7 @@ gpg_key_factory_reset() { DEBUG "Restarting scdaemon to remove possible exclusive lock of dongle" killall -9 scdaemon fi - + # Toggle forced sig (good security practice, forcing PIN request for each signature request) if gpg --card-status | grep "Signature PIN" | grep -q "not forced"; then DEBUG "GPG toggling forcesig on since off..." @@ -575,7 +576,7 @@ gpg_key_factory_reset() { whiptail_error_die "GPG Key forcesig toggle on failed!\n\n$ERROR" fi fi - + # use p256 for key generation if requested if [ "$GPG_ALGO" = "p256" ]; then { @@ -1388,7 +1389,7 @@ fi #if nk3 detected, we add the NK3 Secre App PIN. Detect by product ID if lsusb | grep -q "20a0:42b2"; then - passphrases+="Nitrokey 3 Secret App PIN: ${ADMIN_PIN}\n" + passphrases+="Nitrokey 3 Secrets App PIN: ${ADMIN_PIN}\n" fi #GPG PINs output @@ -1403,7 +1404,6 @@ if [ "$GPG_GEN_KEY_IN_MEMORY" = "y" ]; then passphrases+="GPG key material backup passphrase: ${ADMIN_PIN}\n" fi - # Show configured secrets in whiptail and loop until user confirms qr code was scanned while true; do whiptail --msgbox " diff --git a/initrd/bin/seal-hotpkey b/initrd/bin/seal-hotpkey index 7e6e839f9..266bc48f6 100755 --- a/initrd/bin/seal-hotpkey +++ b/initrd/bin/seal-hotpkey @@ -8,28 +8,26 @@ HOTP_SECRET="/tmp/secret/hotp.key" HOTP_COUNTER="/boot/kexec_hotp_counter" HOTP_KEY="/boot/kexec_hotp_key" -mount_boot() -{ - TRACE_FUNC - # Mount local disk if it is not already mounted - if ! grep -q /boot /proc/mounts; then - if ! mount -o ro /boot; then - whiptail_error --title 'ERROR' \ - --msgbox "Couldn't mount /boot.\n\nCheck the /boot device in configuration settings, or perform an OEM reset." 0 80 - return 1 - fi - fi +mount_boot() { + TRACE_FUNC + # Mount local disk if it is not already mounted + if ! grep -q /boot /proc/mounts; then + if ! mount -o ro /boot; then + whiptail_error --title 'ERROR' \ + --msgbox "Couldn't mount /boot.\n\nCheck the /boot device in configuration settings, or perform an OEM reset." 0 80 + return 1 + fi + fi } TRACE_FUNC -fatal_error() -{ - echo -e "\nERROR: ${1}; press Enter to continue." - read - # get lsusb output for debugging - DEBUG "lsusb output: $(lsusb)" - die "$1" +fatal_error() { + echo -e "\nERROR: ${1}; press Enter to continue." + read + # get lsusb output for debugging + DEBUG "lsusb output: $(lsusb)" + die "$1" } # Use stored HOTP key branding (this might be useful after OEM reset) @@ -41,11 +39,11 @@ fi if [ "$CONFIG_TPM" = "y" ]; then DEBUG "Sealing HOTP secret reuses TOTP sealed secret..." - tpmr unseal 4d47 0,1,2,3,4,7 312 "$HOTP_SECRET" \ - || fatal_error "Unable to unseal HOTP secret" + tpmr unseal 4d47 0,1,2,3,4,7 312 "$HOTP_SECRET" || + fatal_error "Unable to unseal HOTP secret" else # without a TPM, generate a secret based on the SHA-256 of the ROM - secret_from_rom_hash > "$HOTP_SECRET" || die "Reading ROM failed" + secret_from_rom_hash >"$HOTP_SECRET" || die "Reading ROM failed" fi # Store counter in file instead of TPM for now, as it conflicts with Heads @@ -69,20 +67,20 @@ counter_value=1 enable_usb # While making sure the key is inserted, capture the status so we can check how # many PIN attempts remain -if ! hotp_token_info="$(hotp_verification info)" ; then - echo -e "\nInsert your $HOTPKEY_BRANDING and press Enter to configure it" - read - if ! hotp_token_info="$(hotp_verification info)" ; then - # don't leak key on failure - shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null - fatal_error "Unable to find $HOTPKEY_BRANDING" - fi +if ! hotp_token_info="$(hotp_verification info)"; then + echo -e "\nInsert your $HOTPKEY_BRANDING and press Enter to configure it" + read + if ! hotp_token_info="$(hotp_verification info)"; then + # don't leak key on failure + shred -n 10 -z -u "$HOTP_SECRET" 2>/dev/null + fatal_error "Unable to find $HOTPKEY_BRANDING" + fi fi # Set HOTP USB Security Dongle branding based on VID -if lsusb | grep -q "20a0:" ; then +if lsusb | grep -q "20a0:"; then HOTPKEY_BRANDING="Nitrokey" -elif lsusb | grep -q "316d:" ; then +elif lsusb | grep -q "316d:"; then HOTPKEY_BRANDING="Librem Key" else HOTPKEY_BRANDING="HOTP USB Security Dongle" @@ -99,18 +97,25 @@ gpg_key_create_time="${gpg_key_create_time:-0}" DEBUG "Signature key was created at $(date -d "@$gpg_key_create_time")" now_date="$(date '+%s')" -# Get the number of admin PIN retry attempts remaining -awk_admin_counter_regex='/^\s*Card counters: Admin (\d),.*$/' -awk_get_admin_counter="$awk_admin_counter_regex"' { print gensub('"$awk_admin_counter_regex"', "\\1", "") }' -admin_pin_retries="$(echo "$hotp_token_info" | awk "$awk_get_admin_counter")" +# Get the number of HOTP related PIN retry attempts remaining +# if nk3 detected by lsusb, use different regex to get admin counter +if lsusb | grep -q "20a0:42b2"; then + # Nitrokey 3: Secrets app PIN counter: 8 + admin_pin_retries=$(echo "$hotp_token_info" | grep "Secrets app PIN counter:" | cut -d ':' -f 2 | tr -d ' ') + prompt_message="Secrets app" +else + admin_pin_retries=$(echo "$hotp_token_info" | grep "Card counters: Admin" | cut -d ':' -f 2 | tr -d ' ') + prompt_message="GPG Admin" +fi + admin_pin_retries="${admin_pin_retries:-0}" -DEBUG "Admin PIN retry counter is $admin_pin_retries" +DEBUG "HOTP related PIN retry counter is $admin_pin_retries" # Try using factory default admin PIN for 1 month following OEM reset to ease # initial setup. But don't do it forever to encourage changing the PIN and # so PIN attempts are not consumed by the default attempt. admin_pin="12345678" -month_secs="$((30*24*60*60))" +month_secs="$((30 * 24 * 60 * 60))" admin_pin_status=1 if [ "$((now_date - gpg_key_create_time))" -gt "$month_secs" ]; then # Remind what the default PIN was in case it still hasn't been changed @@ -121,48 +126,42 @@ if [ "$((now_date - gpg_key_create_time))" -gt "$month_secs" ]; then elif [ "$admin_pin_retries" -lt 3 ]; then echo "Not trying default PIN ($admin_pin), only $admin_pin_retries attempt(s) left" else - hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" >/dev/null 2>&1 + echo "Trying $prompt_message PIN ($admin_pin) to seal HOTP secret on $HOTPKEY_BRANDING... You may be requested to touch the dongle..." + #TODO: silence the output of hotp_initialize once https://github.com/Nitrokey/nitrokey-hotp-verification/issues/41 is fixed + #hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" >/dev/null 2>&1 + hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" admin_pin_status="$?" fi if [ "$admin_pin_status" -ne 0 ]; then - # create custom message for PIN prompt based on nk3 lsusb product id - prompt_message="" - if lsusb | grep -q "20a0:42b2"; then - prompt_message="Secure App" - else - prompt_message="GPG Admin" - fi - - - # prompt user for PIN and retry - echo "" - read -s -p "Enter your $HOTPKEY_BRANDING $prompt_message PIN: " admin_pin - echo -e "\n" - - hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" - if [ $? -ne 0 ]; then - echo -e "\n" - read -s -p "Error setting HOTP secret, re-enter $prompt_message PIN and try again: " admin_pin - echo -e "\n" - if ! hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" ; then - # don't leak key on failure - shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null - if [ "$HOTPKEY_BRANDING" == "Nitrokey" ]; then - fatal_error "Setting HOTP secret failed, to reset $prompt_message PIN, redo Re-Ownership procedure, the Nitrokey App 2 or contact Nitrokey support" - else - fatal_error "Setting HOTP secret failed" - fi - fi - fi -else - # remind user to change admin password - echo -e "\nWARNING: default admin PIN detected: please change this as soon as possible." + # prompt user for PIN and retry + echo "" + read -s -p "Enter your $HOTPKEY_BRANDING $prompt_message PIN: " admin_pin + echo -e "\n" + + hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING" + if [ $? -ne 0 ]; then + echo -e "\n" + read -s -p "Error setting HOTP secret, re-enter $prompt_message PIN and try again: " admin_pin + echo -e "\n" + if ! hotp_initialize "$admin_pin" $HOTP_SECRET $counter_value "$HOTPKEY_BRANDING"; then + # don't leak key on failure + shred -n 10 -z -u "$HOTP_SECRET" 2>/dev/null + if [ "$HOTPKEY_BRANDING" == "Nitrokey" ]; then + fatal_error "Setting HOTP secret failed, to reset $prompt_message PIN, redo Re-Ownership procedure, use the Nitrokey App 2 or contact Nitrokey support" + else + fatal_error "Setting HOTP secret failed" + fi + fi + fi +else + # remind user to change admin password + warn "Factory $prompt_message default PIN detected: please change this PIN as soon as possible through OEM Factory Reset/User Re-Ownership" fi # HOTP key no longer needed -shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null +shred -n 10 -z -u "$HOTP_SECRET" 2>/dev/null # Make sure our counter is incremented ahead of the next check #increment_tpm_counter $counter > /dev/null \ @@ -172,13 +171,13 @@ shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null mount -o remount,rw /boot -counter_value=`expr $counter_value + 1` -echo $counter_value > $HOTP_COUNTER \ -|| fatal_error "Unable to create hotp counter file" +counter_value=$(expr $counter_value + 1) +echo $counter_value >$HOTP_COUNTER || + fatal_error "Unable to create hotp counter file" # Store/overwrite HOTP USB Security Dongle branding found out beforehand -echo $HOTPKEY_BRANDING > $HOTP_KEY \ -|| die "Unable to store hotp key file" +echo $HOTPKEY_BRANDING >$HOTP_KEY || + die "Unable to store hotp key file" #sha256sum /tmp/counter-$counter > $HOTP_COUNTER \ #|| die "Unable to create hotp counter file" From 6591f267e68c273c32d603272a04e11c916b5335 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 6 Dec 2024 11:36:50 -0500 Subject: [PATCH 21/25] hotp-verification: removed patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346 directory: waiting for https://github.com/Nitrokey/nitrokey-hotp-verification/pull/43 and https://github.com/Nitrokey/nitrokey-hotp-verification/pull/46 to be merged to change modules/hotp-verification commit Signed-off-by: Thierry Laurion --- .../43.patch | 450 ------------------ 1 file changed, 450 deletions(-) delete mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch deleted file mode 100644 index 26ad2bc45..000000000 --- a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/43.patch +++ /dev/null @@ -1,450 +0,0 @@ -From 707c6545a509eeb24a06537e5f835d786c2e657e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= -Date: Thu, 5 Dec 2024 16:44:30 +0100 -Subject: [PATCH] Add support for nitrokey 3 distinction between the secrets - app and other - -This now adds the secrets app version and the nitrokey 3 firmware version, -and also the gpg pins ---- - src/ccid.c | 71 +++++++++++++++++++++++++- - src/ccid.h | 2 + - src/device.c | 29 +++++------ - src/device.h | 2 +- - src/main.c | 41 +++++++++++---- - src/operations_ccid.c | 113 +++++++++++++++++++++++++++++++++++++----- - src/operations_ccid.h | 2 +- - src/structs.h | 19 +++++++ - 8 files changed, 238 insertions(+), 41 deletions(-) - -diff --git a/src/ccid.c b/src/ccid.c -index 9cf24a0..a2cc919 100644 ---- a/src/ccid.c -+++ b/src/ccid.c -@@ -104,7 +104,7 @@ IccResult parse_icc_result(uint8_t *buf, size_t buf_len) { - // .buffer_len = buf_len - }; - // Make sure the response do not contain overread attempts -- rassert(i.data_len < buf_len - 10); -+ rassert(i.data_len <= buf_len - 10); - return i; - } - -@@ -307,6 +307,75 @@ int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_siz - return RET_NO_ERROR; - } - -+int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { -+ unsigned char cmd_select[] = { -+ 0x6f, -+ 0x0E, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0xa4, -+ 0x04, -+ 0x00, -+ 0x09, -+ 0xa0, -+ 0x00, -+ 0x00, -+ 0x08, -+ 0x47, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x01, -+ }; -+ -+ check_ret( -+ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), -+ RET_COMM_ERROR); -+ -+ -+ return RET_NO_ERROR; -+} -+ -+int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult) { -+ unsigned char cmd_select[] = { -+ 0x6f, -+ 0x0C, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0x00, -+ 0xA4, -+ 0x04, -+ 0x00, -+ 0x06, -+ 0xD2, -+ 0x76, -+ 0x00, -+ 0x01, -+ 0x24, -+ 0x01, -+ 0x00, -+ }; -+ -+ check_ret( -+ ccid_process_single(handle, buf, buf_size, cmd_select, sizeof cmd_select, iccResult), -+ RET_COMM_ERROR); -+ -+ -+ return RET_NO_ERROR; -+} - - int ccid_init(libusb_device_handle *handle) { - -diff --git a/src/ccid.h b/src/ccid.h -index ed17dc7..3dcf106 100644 ---- a/src/ccid.h -+++ b/src/ccid.h -@@ -70,6 +70,8 @@ uint32_t icc_pack_tlvs_for_sending(uint8_t *buf, size_t buflen, TLV tlvs[], int - libusb_device_handle *get_device(libusb_context *ctx, const struct VidPid pPid[], int devices_count); - int ccid_init(libusb_device_handle *handle); - int send_select_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); -+int send_select_nk3_admin_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); -+int send_select_nk3_pgp_ccid(libusb_device_handle *handle, uint8_t buf[], size_t buf_size, IccResult *iccResult); - - - enum { -diff --git a/src/device.c b/src/device.c -index 4b9361e..52acece 100644 ---- a/src/device.c -+++ b/src/device.c -@@ -29,6 +29,7 @@ - #include "structs.h" - #include "utils.h" - #include -+#include - #include - #include - #include -@@ -259,23 +260,19 @@ int device_receive_buf(struct Device *dev) { - - #include "operations_ccid.h" - --int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { -- assert(out_status != NULL); -+int device_get_status(struct Device *dev, struct FullResponseStatus *out_response) { -+ assert(out_response != NULL); - assert(dev != NULL); -- memset(out_status, 0, sizeof(struct ResponseStatus)); -+ memset(out_response, 0, sizeof(struct FullResponseStatus)); -+ -+ struct ResponseStatus *out_status = &out_response->response_status; - - if (dev->connection_type == CONNECTION_CCID) { -- int counter = 0; -- uint32_t serial = 0; -- uint16_t version = 0; -- int res = status_ccid(dev->mp_devhandle_ccid, -- &counter, -- &version, -- &serial); -- out_status->retry_admin = counter; -- out_status->retry_user = counter; -- out_status->card_serial_u32 = serial; -- out_status->firmware_version = version; -+ int res = status_ccid(dev->mp_devhandle_ccid, out_response); -+ // out_status->retry_admin = counter; -+ // out_status->retry_user = counter; -+ // out_status->card_serial_u32 = serial; -+ // out_status->firmware_version = version; - return res; - } - -@@ -290,7 +287,7 @@ int device_get_status(struct Device *dev, struct ResponseStatus *out_status) { - - device_send_buf(dev, GET_STATUS); - device_receive_buf(dev); -- *out_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; -+ out_response->response_status = *(struct ResponseStatus *) dev->packet_response.response_st.payload; - - if (out_status->firmware_version_st.minor == 1) { - for (int i = 0; i < 100; ++i) { -@@ -343,4 +340,4 @@ const char *command_status_to_string(uint8_t status_code) { - void clean_buffers(struct Device *dev) { - memset(dev->ccid_buffer_in, 0, sizeof dev->ccid_buffer_in); - memset(dev->ccid_buffer_out, 0, sizeof dev->ccid_buffer_out); --} -\ No newline at end of file -+} -diff --git a/src/device.h b/src/device.h -index c895546..97feeeb 100644 ---- a/src/device.h -+++ b/src/device.h -@@ -72,7 +72,7 @@ struct Device { - - int device_connect(struct Device *dev); - int device_disconnect(struct Device *dev); --int device_get_status(struct Device *dev, struct ResponseStatus *out_status); -+int device_get_status(struct Device *dev, struct FullResponseStatus *out_status); - int device_send(struct Device *dev, uint8_t *in_data, size_t data_size, uint8_t command_ID); - int device_receive(struct Device *dev, uint8_t *out_data, size_t out_buffer_size); - int device_send_buf(struct Device *dev, uint8_t command_ID); -diff --git a/src/main.c b/src/main.c -index 059069e..9b38552 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -93,25 +93,46 @@ int parse_cmd_and_run(int argc, char *const *argv) { - res = RET_NO_ERROR; - break; - case 'i': {// id | info -- struct ResponseStatus status; -+ struct FullResponseStatus status; -+ memset(&status, 0, sizeof (struct FullResponseStatus)); -+ - res = device_get_status(&dev, &status); - check_ret((res != RET_NO_ERROR) && (res != RET_NO_PIN_ATTEMPTS), res); - if (strnlen(argv[1], 10) == 2 && argv[1][1] == 'd') { - // id command - print ID only -- print_card_serial(&status); -+ print_card_serial(&status.response_status); - } else { - // info command - print status - printf("Connected device status:\n"); - printf("\tCard serial: "); -- print_card_serial(&status); -- printf("\tFirmware: v%d.%d\n", -- status.firmware_version_st.major, -- status.firmware_version_st.minor); -- if (res != RET_NO_PIN_ATTEMPTS) { -- printf("\tCard counters: Admin %d, User %d\n", -- status.retry_admin, status.retry_user); -+ print_card_serial(&status.response_status); -+ if (status.device_type == Nk3) { -+ printf("\tFirmware Nitrokey 3: v%d.%d.%d\n", -+ (status.nk3_extra_info.firmware_version >> 22) & 0b1111111111, -+ (status.nk3_extra_info.firmware_version >> 6) & 0xFFFF, -+ status.nk3_extra_info.firmware_version & 0b111111); -+ printf("\tFirmware Secrets App: v%d.%d\n", -+ status.response_status.firmware_version_st.major, -+ status.response_status.firmware_version_st.minor); -+ if (res != RET_NO_PIN_ATTEMPTS) { -+ printf("\tSecrets app PIN counter: %d\n", -+ status.response_status.retry_user); -+ } else { -+ printf("\tSecrets app PIN counter: PIN is not set - set PIN before the first use\n"); -+ } -+ printf("\tGPG Card counters: Admin %d, User %d\n", -+ status.nk3_extra_info.pgp_admin_pin_retries, -+ status.nk3_extra_info.pgp_user_pin_retries); - } else { -- printf("\tCard counters: PIN is not set - set PIN before the first use\n"); -+ printf("\tFirmware: v%d.%d\n", -+ status.response_status.firmware_version_st.major, -+ status.response_status.firmware_version_st.minor); -+ if (res != RET_NO_PIN_ATTEMPTS) { -+ printf("\tCard counters: Admin %d, User %d\n", -+ status.response_status.retry_admin, status.response_status.retry_user); -+ } else { -+ printf("\tCard counters: PIN is not set - set PIN before the first use\n"); -+ } - } - } - if (res == RET_NO_PIN_ATTEMPTS) { -diff --git a/src/operations_ccid.c b/src/operations_ccid.c -index eb46124..25772e5 100644 ---- a/src/operations_ccid.c -+++ b/src/operations_ccid.c -@@ -273,14 +273,102 @@ int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify) { - return RET_VALIDATION_PASSED; - } - --int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number) { -+int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response) { -+ rassert(full_response != NULL); -+ struct ResponseStatus *response = &full_response->response_status; - rassert(handle != NULL); -- rassert(attempt_counter != NULL); -- rassert(firmware_version != NULL); -- rassert(serial_number != NULL); - uint8_t buf[1024] = {}; - IccResult iccResult = {}; -- int r = send_select_ccid(handle, buf, sizeof buf, &iccResult); -+ bool pin_counter_is_error = false; -+ int r; -+ libusb_device *usb_dev; -+ struct libusb_device_descriptor usb_desc; -+ -+ usb_dev = libusb_get_device(handle); -+ -+ r = libusb_get_device_descriptor(usb_dev, &usb_desc); -+ -+ if (r < 0) { -+ return r; -+ } -+ -+ -+ if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_3_USB_PID) { -+ full_response->device_type = Nk3; -+ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_PRO_USB_PID) { -+ full_response->device_type = NkPro2; -+ } else if (usb_desc.idVendor == NITROKEY_USB_VID || usb_desc.idProduct == NITROKEY_STORAGE_USB_PID) { -+ full_response->device_type = NkStorage; -+ } else if (usb_desc.idVendor == LIBREM_KEY_USB_VID || usb_desc.idProduct == LIBREM_KEY_USB_PID) { -+ full_response->device_type = LibremKey; -+ } -+ -+ if (full_response->device_type == Nk3) { -+ r = send_select_nk3_admin_ccid(handle, buf, sizeof buf, &iccResult); -+ if (r != RET_NO_ERROR) { -+ return r; -+ } -+ -+ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; -+ uint32_t iso_actual_length = iso7816_compose( -+ data_iso, sizeof data_iso, -+ 0x61, 0, 0, 0, 4, NULL, 0); -+ -+ // encode ccid wrapper -+ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, -+ 0x6F, iso_actual_length, -+ 0, 0, 0, data_iso); -+ int transferred; -+ r = ccid_send(handle, &transferred, buf, icc_actual_length); -+ if (r != 0) { -+ return r; -+ } -+ -+ r = ccid_receive(handle, &transferred, buf, sizeof buf); -+ if (r != 0) { -+ return r; -+ } -+ -+ IccResult iccResult = parse_icc_result(buf, transferred); -+ rassert(iccResult.data_status_code == 0x9000); -+ rassert(iccResult.data_len == 6); -+ full_response->nk3_extra_info.firmware_version = be32toh(*(uint32_t *) iccResult.data); -+ } -+ -+ if (full_response->device_type == Nk3) { -+ r = send_select_nk3_pgp_ccid(handle, buf, sizeof buf, &iccResult); -+ if (r != RET_NO_ERROR) { -+ return r; -+ } -+ -+ uint8_t data_iso[MAX_CCID_BUFFER_SIZE] = {}; -+ uint32_t iso_actual_length = iso7816_compose( -+ data_iso, sizeof data_iso, -+ 0xCA, 0, 0xC4, 0, 0xFF, NULL, 0); -+ -+ // encode ccid wrapper -+ uint32_t icc_actual_length = icc_compose(buf, sizeof buf, -+ 0x6F, iso_actual_length, -+ 0, 0, 0, data_iso); -+ int transferred; -+ r = ccid_send(handle, &transferred, buf, icc_actual_length); -+ if (r != 0) { -+ return r; -+ } -+ -+ r = ccid_receive(handle, &transferred, buf, sizeof buf); -+ if (r != 0) { -+ return r; -+ } -+ -+ IccResult iccResult = parse_icc_result(buf, transferred); -+ rassert(iccResult.data_status_code == 0x9000); -+ rassert(iccResult.data_len == 9); -+ full_response->nk3_extra_info.pgp_user_pin_retries = iccResult.data[4]; -+ full_response->nk3_extra_info.pgp_admin_pin_retries = iccResult.data[6]; -+ } -+ -+ r = send_select_ccid(handle, buf, sizeof buf, &iccResult); - if (r != RET_NO_ERROR) { - return r; - } -@@ -292,29 +380,30 @@ int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *fi - r = get_tlv(iccResult.data, iccResult.data_len, Tag_PINCounter, &counter_tlv); - if (!(r == RET_NO_ERROR && counter_tlv.tag == Tag_PINCounter)) { - // PIN counter not found - comm error (ignore) or PIN not set -- *attempt_counter = -1; -+ pin_counter_is_error = true; - } else { -- *attempt_counter = counter_tlv.v_data[0]; -+ response->retry_admin = counter_tlv.v_data[0]; -+ response->retry_user = counter_tlv.v_data[0]; - } - - TLV serial_tlv = {}; - r = get_tlv(iccResult.data, iccResult.data_len, Tag_SerialNumber, &serial_tlv); - if (r == RET_NO_ERROR && serial_tlv.tag == Tag_SerialNumber) { -- *serial_number = be32toh(*(uint32_t *) serial_tlv.v_data); -+ response->card_serial_u32 = be32toh(*(uint32_t *) serial_tlv.v_data); - } else { - // ignore errors - unsupported or hidden serial_tlv number -- *serial_number = 0; -+ response->card_serial_u32 = 0; - } - - TLV version_tlv = {}; - r = get_tlv(iccResult.data, iccResult.data_len, Tag_Version, &version_tlv); - if (!(r == RET_NO_ERROR && version_tlv.tag == Tag_Version)) { -- *firmware_version = 0; -+ response->firmware_version = 0; - return RET_COMM_ERROR; - } -- *firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); -+ response->firmware_version = be16toh(*(uint16_t *) version_tlv.v_data); - -- if (*attempt_counter == -1) { -+ if (pin_counter_is_error == true) { - return RET_NO_PIN_ATTEMPTS; - } - return RET_NO_ERROR; -diff --git a/src/operations_ccid.h b/src/operations_ccid.h -index b26b3c7..ea463b4 100644 ---- a/src/operations_ccid.h -+++ b/src/operations_ccid.h -@@ -10,7 +10,7 @@ int authenticate_ccid(struct Device *dev, const char *admin_PIN); - int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); - int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); - int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); --int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); -+int status_ccid(libusb_device_handle *handle, struct FullResponseStatus *full_response); - - - #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H -diff --git a/src/structs.h b/src/structs.h -index 6309cd0..9e87134 100644 ---- a/src/structs.h -+++ b/src/structs.h -@@ -116,6 +116,25 @@ struct ResponseStatus { - uint8_t retry_user; /*not present in the firmware response for the Status command in v0.8 firmware*/ - }; - -+enum DeviceType { -+ Unknown = 0, -+ Nk3, -+ NkPro2, -+ NkStorage, -+ LibremKey, -+}; -+ -+struct FullResponseStatus { -+ enum DeviceType device_type; -+ struct ResponseStatus response_status; -+ struct { -+ // Only valid if device_type is NK3 -+ uint8_t pgp_admin_pin_retries; -+ uint8_t pgp_user_pin_retries; -+ uint32_t firmware_version; -+ } nk3_extra_info; -+}; -+ - - struct WriteToOTPSlot { - //admin auth From 7051fc878515f87c4ba0e227dfe0191399b731f2 Mon Sep 17 00:00:00 2001 From: Jonathon Hall Date: Fri, 6 Dec 2024 16:22:52 -0500 Subject: [PATCH 22/25] functions: Fix spelling of 'dictionaries' Signed-off-by: Jonathon Hall --- .../eff_short_wordlist_2_0.txt | 0 initrd/etc/functions | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename initrd/etc/{diceware_dictionnaries => diceware_dictionaries}/eff_short_wordlist_2_0.txt (100%) diff --git a/initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt b/initrd/etc/diceware_dictionaries/eff_short_wordlist_2_0.txt similarity index 100% rename from initrd/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt rename to initrd/etc/diceware_dictionaries/eff_short_wordlist_2_0.txt diff --git a/initrd/etc/functions b/initrd/etc/functions index aa57676d9..ae25bc2b2 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -875,7 +875,7 @@ generate_passphrase() { usage_generate_passphrase() { echo "Usage: generate_passphrase --dictionary|-d [--number_words|-n ] [--max_length|-m ] [--lowercase|-l]" echo "Generates a passphrase using a Diceware dictionary." - echo " --dictionary|-d Path to the Diceware dictionary file (defaults to /etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt )." + echo " --dictionary|-d Path to the Diceware dictionary file (defaults to /etc/diceware_dictionaries/eff_short_wordlist_2_0.txt )." echo " [--number_words|-n ] Number of words in the passphrase (default: 3)." echo " [--max_length|-m ] Maximum size of the passphrase (default: 256)." echo " [--lowercase|-l] Use lowercase words (default: false)." @@ -910,7 +910,7 @@ generate_passphrase() { } TRACE_FUNC - local dictionary_file="/etc/diceware_dictionnaries/eff_short_wordlist_2_0.txt" + local dictionary_file="/etc/diceware_dictionaries/eff_short_wordlist_2_0.txt" local num_words=3 local max_size=256 local lowercase=false From c00c036c01e2848f2cb621f7c297e8f64826023d Mon Sep 17 00:00:00 2001 From: Jonathon Hall Date: Fri, 6 Dec 2024 16:24:20 -0500 Subject: [PATCH 23/25] functions: Simplify dictionary word selection The dice-rolls method was relatively complex and somewhat biased (~2.4% biased toward 1-4 on each roll due to modulo bias). Just pick a line from the dictionary at random. Using all 32 bits of entropy to pick a line once distributes the modulo bias so it is only 0.000003% biased toward the first 1263 words. Signed-off-by: Jonathon Hall --- initrd/etc/functions | 56 ++++++++++---------------------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/initrd/etc/functions b/initrd/etc/functions index ae25bc2b2..2dabda7b3 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -881,32 +881,18 @@ generate_passphrase() { echo " [--lowercase|-l] Use lowercase words (default: false)." } - # Helper subfunction to get a word from the dictionary based on dice rolls - get_word_from_dictionary() { - local rolls="$1" - local dictionary_file="$2" - local word="" - - word=$(grep "^$rolls" "$dictionary_file" | awk -F ' ' '{print $2}') - echo "$word" - } - - # Helper subfunction to generate dice rolls - generate_dice_rolls() { - TRACE_FUNC - local num_rolls="$1" - local rolls="" - local random_bytes - - # Read num_rolls bytes from /dev/random, fed by CPU RRAND in one go - random_bytes=$(dd if=/dev/random bs=1 count="$num_rolls" 2>/dev/null | hexdump -e '1/1 "%u\n"') - - # Process each byte to generate a dice roll - while read -r byte; do - roll=$((byte % 6 + 1)) - rolls+=$roll - done <<<"$random_bytes" - echo "$rolls" + # Helper subfunction to get a random word from the dictionary + get_random_word_from_dictionary() { + local dictionary_file="$1" lines random + + lines="$(wc -l <"$dictionary_file")" + # 4 random bytes are used to reduce modulo bias to an acceptable + # level. 4 bytes with modulus 1296 results in 0.000003% bias + # toward the first 1263 words. + random="$(dd if=/dev/random bs=4 count=1 status=none | hexdump -e '1/4 "%u\n"')" + ((random%=lines)) + ((++random)) # tail's line count is 1-based + tail -n +"$random" "$dictionary_file" | head -1 | cut -d$'\t' -f2 } TRACE_FUNC @@ -961,25 +947,9 @@ generate_passphrase() { local passphrase="" local word="" - local key="" - local digits=0 - - # Read the number of digits from the first line of the dictionary file - read -r key _ <"$dictionary_file" - - # Validate that the key is composed entirely of digits - if ! [[ $key =~ ^[0-9]+$ ]]; then - echo "Error: Dictionary is not compliant with EFF diceware dictionaries." - echo "The first line of the dictionary should be in the format: " - echo "Example: 11111 word" - exit 1 - fi - - digits=${#key} #Number of digits in dice rolls for ((i = 0; i < num_words; ++i)); do - key=$(generate_dice_rolls "$digits") - word=$(get_word_from_dictionary "$key" "$dictionary_file") + word=$(get_random_word_from_dictionary "$dictionary_file") if [[ "$lowercase" == "false" ]]; then word=${word^} # Capitalize the first letter fi From 86a61586b5c97c3379f74eab65d54420637f0fc8 Mon Sep 17 00:00:00 2001 From: Jonathon Hall Date: Fri, 6 Dec 2024 16:26:41 -0500 Subject: [PATCH 24/25] oem-factory-reset: Stop adding leading blank lines in 'passphrases' msg We're adding leading blank lines, which makes the prompt look odd and now have to be removed later. Just stop adding the leading blank lines. Signed-off-by: Jonathon Hall --- initrd/bin/oem-factory-reset | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 613569530..66340e65c 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -1376,7 +1376,7 @@ if [[ "$SKIP_BOOT" == "n" ]]; then fi # passphrases set to be empty first -passphrases="\n" +passphrases="" # Prepare whiptail output of configured secrets if [ -n "$luks_new_Disk_Recovery_Key_passphrase" -o -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then @@ -1406,14 +1406,11 @@ fi # Show configured secrets in whiptail and loop until user confirms qr code was scanned while true; do - whiptail --msgbox " - $(echo -e "$passphrases" | fold -w $((WIDTH - 5)))" \ + whiptail --msgbox "$(echo -e "$passphrases" | fold -w $((WIDTH - 5)))" \ $HEIGHT $WIDTH --title "Configured secrets" - # strip the initial newline of passphrases - qr_code=$(echo -e "$passphrases" | sed '1s/^\n//') #Tell user to scan the QR code containing all configured secrets echo -e "\nScan the QR code below to save the secrets to a secure location" - qrenc "$qr_code" + qrenc "$(echo -e "$passphrases")" # Prompt user to confirm scanning of qrcode on console prompt not whiptail: y/n echo -e -n "Please confirm you have scanned the QR code above and/or written down the secrets? [y/N]: " read -n 1 prompt_output From 4b4ac60240d096504605a3105145f649807a5be0 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 7 Dec 2024 11:12:38 -0500 Subject: [PATCH 25/25] patches/hotp-verification-*/46.patch : readd https://github.com/Nitrokey/nitrokey-hotp-verification/pull/46 so that this PR can be tested and reviewed from OEM Factory Reset/User Re-Ownership perspective (PR 43 not in which fixes hotp_verification info, needed to reuse default PINs under seal-hotp if pubkey age <1 month and if Secret app PIN/GPG Admin PIN count >=3 ) Repro: mkdir patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346 wget https://patch-diff.githubusercontent.com/raw/Nitrokey/nitrokey-hotp-verification/pull/46.patch -O patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch sudo rm -rf build/x86/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/ ./docker_repro.sh make BOARD=qemu-coreboot-whiptail-tpm2-hotp USB_TOKEN=Nitrokey3NFC PUBKEY_ASC=pubkey.asc inject_gpg run Signed-off-by: Thierry Laurion --- .../46.patch | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch diff --git a/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch new file mode 100644 index 000000000..781c10ffa --- /dev/null +++ b/patches/hotp-verification-e9050e0c914e7a8ffef5d1c82a014e0e2bf79346/46.patch @@ -0,0 +1,219 @@ +From de355ed93ba50280bf377772082b76b7a2285185 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Mon, 25 Nov 2024 17:04:47 +0100 +Subject: [PATCH 1/3] Add reset command for nitrokey 3 + +--- + src/main.c | 10 ++++++++-- + src/operations_ccid.c | 41 +++++++++++++++++++++++++++++++++++++++++ + src/operations_ccid.h | 1 + + 3 files changed, 50 insertions(+), 2 deletions(-) + +diff --git a/src/main.c b/src/main.c +index 059069e..b80b71d 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -21,6 +21,7 @@ + + #include "ccid.h" + #include "operations.h" ++#include "operations_ccid.h" + #include "return_codes.h" + #include "utils.h" + #include "version.h" +@@ -134,8 +135,13 @@ int parse_cmd_and_run(int argc, char *const *argv) { + } + break; + case 'r': +- if (argc != 3) break; +- res = regenerate_AES_key(&dev, argv[2]); ++ if (strncmp(argv[1], "reset", 15) == 0) { ++ if (argc != 2) break; ++ res = nk3_reset(&dev); ++ } else if (strncmp(argv[1], "regenerate", 15) == 0) { ++ if (argc != 3) break; ++ res = regenerate_AES_key(&dev, argv[2]); ++ } + break; + default: + break; +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index eb46124..574155d 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -32,6 +32,47 @@ + #include + + ++ ++int nk3_reset(struct Device *dev) { ++ libusb_device *usb_dev; ++ struct libusb_device_descriptor usb_desc; ++ usb_dev = libusb_get_device(dev->mp_devhandle_ccid); ++ ++ int r = libusb_get_device_descriptor(usb_dev, &usb_desc); ++ ++ if (r < 0) { ++ return r; ++ } ++ ++ ++ if (usb_desc.idVendor != NITROKEY_USB_VID || usb_desc.idProduct != NITROKEY_3_USB_PID) { ++ return 0; ++ } ++ ++ ++ uint8_t buf[10]; ++ // encode ++ uint32_t icc_actual_length = iso7816_compose(buf, sizeof buf, Ins_Reset, 0xDE, 0xAD, 0, 0, NULL, 0); ++ ++ // encode ccid wrapper ++ icc_actual_length = icc_compose(dev->ccid_buffer_out, sizeof dev->ccid_buffer_out, ++ 0x6F, icc_actual_length, ++ 0, 0, 0, buf); ++ // send ++ IccResult iccResult; ++ r = ccid_process_single(dev->mp_devhandle_ccid, dev->ccid_buffer_in, sizeof dev->ccid_buffer_in, ++ dev->ccid_buffer_out, icc_actual_length, &iccResult); ++ if (r != 0) { ++ return r; ++ } ++ // check status code ++ if (iccResult.data_status_code != 0x9000) { ++ return 1; ++ } ++ ++ return RET_NO_ERROR; ++} ++ + int set_pin_ccid(struct Device *dev, const char *admin_PIN) { + TLV tlvs[] = { + { +diff --git a/src/operations_ccid.h b/src/operations_ccid.h +index b26b3c7..ec0070c 100644 +--- a/src/operations_ccid.h ++++ b/src/operations_ccid.h +@@ -11,6 +11,7 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); + int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); + int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); + int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); ++int nk3_reset(struct Device *dev); + + + #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H + +From 8425e8c622138aef9ab207119e14f7cbedd40175 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Mon, 2 Dec 2024 10:29:59 +0100 +Subject: [PATCH 2/3] Add optional new pin when resetting + +--- + src/main.c | 9 +++++---- + src/operations_ccid.c | 6 +++++- + src/operations_ccid.h | 5 ++++- + 3 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/src/main.c b/src/main.c +index b80b71d..3f4a1cc 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -38,9 +38,10 @@ void print_help(char *app_name) { + "\t%s info\n" + "\t%s version\n" + "\t%s check \n" +- "\t%s regenerate \n" ++ "\t%s reset [ADMIN PIN]\n" ++ "\t%s regenerate\n" + "\t%s set [COUNTER]\n", +- app_name, app_name, app_name, app_name, app_name, app_name); ++ app_name, app_name, app_name, app_name, app_name, app_name, app_name); + } + + +@@ -136,8 +137,8 @@ int parse_cmd_and_run(int argc, char *const *argv) { + break; + case 'r': + if (strncmp(argv[1], "reset", 15) == 0) { +- if (argc != 2) break; +- res = nk3_reset(&dev); ++ if (argc != 2 && argc != 3) break; ++ res = nk3_reset(&dev, argc == 3 ? argv[2]: NULL); + } else if (strncmp(argv[1], "regenerate", 15) == 0) { + if (argc != 3) break; + res = regenerate_AES_key(&dev, argv[2]); +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index 574155d..07834ce 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -33,7 +33,7 @@ + + + +-int nk3_reset(struct Device *dev) { ++int nk3_reset(struct Device *dev, const char * new_pin) { + libusb_device *usb_dev; + struct libusb_device_descriptor usb_desc; + usb_dev = libusb_get_device(dev->mp_devhandle_ccid); +@@ -70,6 +70,10 @@ int nk3_reset(struct Device *dev) { + return 1; + } + ++ if (new_pin != NULL) { ++ set_pin_ccid(dev, new_pin); ++ } ++ + return RET_NO_ERROR; + } + +diff --git a/src/operations_ccid.h b/src/operations_ccid.h +index ec0070c..61cad72 100644 +--- a/src/operations_ccid.h ++++ b/src/operations_ccid.h +@@ -11,7 +11,10 @@ int authenticate_or_set_ccid(struct Device *dev, const char *admin_PIN); + int set_secret_on_device_ccid(struct Device *dev, const char *admin_PIN, const char *OTP_secret_base32, const uint64_t hotp_counter); + int verify_code_ccid(struct Device *dev, const uint32_t code_to_verify); + int status_ccid(libusb_device_handle *handle, int *attempt_counter, uint16_t *firmware_version, uint32_t *serial_number); +-int nk3_reset(struct Device *dev); ++// new_pin can be `null` ++// ++// If it is, no new pin will be set ++int nk3_reset(struct Device *dev, const char * new_pin); + + + #endif//NITROKEY_HOTP_VERIFICATION_OPERATIONS_CCID_H + +From 596f701985682adf6bfab06c78cbe132cbcb2aae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= +Date: Tue, 3 Dec 2024 10:48:27 +0100 +Subject: [PATCH 3/3] Fix null pointer bug on non nk3 + +--- + src/operations_ccid.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/operations_ccid.c b/src/operations_ccid.c +index 07834ce..538d434 100644 +--- a/src/operations_ccid.c ++++ b/src/operations_ccid.c +@@ -36,6 +36,12 @@ + int nk3_reset(struct Device *dev, const char * new_pin) { + libusb_device *usb_dev; + struct libusb_device_descriptor usb_desc; ++ ++ if (!dev->mp_devhandle_ccid) { ++ // Not an NK3 ++ return RET_NO_ERROR; ++ } ++ + usb_dev = libusb_get_device(dev->mp_devhandle_ccid); + + int r = libusb_get_device_descriptor(usb_dev, &usb_desc); +@@ -46,7 +52,7 @@ int nk3_reset(struct Device *dev, const char * new_pin) { + + + if (usb_desc.idVendor != NITROKEY_USB_VID || usb_desc.idProduct != NITROKEY_3_USB_PID) { +- return 0; ++ return RET_NO_ERROR; + } + +