Guest User

Untitled

a guest
Oct 26th, 2026
20
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.24 KB | None | 0 0
  1. --- /home/alice/git/FIDO2Applet/src/main/java/us/q3q/fido2/FIDO2Applet.java 2026-10-26 06:02:29.212198643 +0000
  2. +++ src/FIDO2Applet.java 2026-10-26 07:29:02.124197844 +0000
  3. @@ -2,14 +2,14 @@
  4.  
  5. import javacard.framework.*;
  6. import javacard.security.*;
  7. -import javacardx.apdu.ExtendedLength;
  8. +
  9.  
  10. import javacardx.crypto.Cipher;
  11.  
  12. /**
  13. * Core applet class implementing FIDO2 specifications on Javacard
  14. */
  15. -public final class FIDO2Applet extends Applet implements ExtendedLength {
  16. +public final class FIDO2Applet extends Applet {
  17.  
  18. /**
  19. * The version of this applet in use
  20. @@ -209,24 +209,24 @@
  21. /**
  22. * Random keys for deriving keys for the hmac-secret extension from regular credential private keys
  23. */
  24. - private final byte[] hmacWrapperBytesUV;
  25. - private final byte[] hmacWrapperBytesNoUV;
  26. + private byte[] hmacWrapperBytesUV;
  27. + private byte[] hmacWrapperBytesNoUV;
  28. /**
  29. * Random key for verifying (HMAC-ing) created credentials
  30. */
  31. - private final byte[] credentialVerificationKey;
  32. + private byte[] credentialVerificationKey;
  33. /**
  34. * DH for platform <-> authenticator secure channel
  35. */
  36. - private final KeyAgreement keyAgreement;
  37. + private KeyAgreement keyAgreement;
  38. /**
  39. * encipher things destined for the platform
  40. */
  41. - private final Cipher sharedSecretWrapper;
  42. + private Cipher sharedSecretWrapper;
  43. /**
  44. * decipher things sent by the platform
  45. */
  46. - private final Cipher sharedSecretUnwrapper;
  47. + private Cipher sharedSecretUnwrapper;
  48. /**
  49. * must be zeroes. Is used for encryption and decryption of messages between authenticator and platform
  50. */
  51. @@ -242,16 +242,16 @@
  52. /**
  53. * Decrypter used to unwrap data by using a key derived from the user's PIN
  54. */
  55. - private final Cipher pinUnwrapper;
  56. + private Cipher pinUnwrapper;
  57. /**
  58. * Encrypter used to wrap data by using a key derived from the user's PIN
  59. */
  60. - private final Cipher pinWrapper;
  61. + private Cipher pinWrapper;
  62.  
  63. /**
  64. * per-device salt for deriving keys from PINs
  65. */
  66. - private final byte[] pinKDFSalt;
  67. + private byte[] pinKDFSalt;
  68. /**
  69. * key to be set from KDF derivation of PIN
  70. */
  71. @@ -263,7 +263,7 @@
  72. /**
  73. * used for signing the authData we send to the platform, to prove it came from us
  74. */
  75. - private final Signature attester;
  76. + private Signature attester;
  77.  
  78. // Fields for wrapping and unwrapping platform-held blobs
  79. /**
  80. @@ -272,48 +272,48 @@
  81. *
  82. * If a PIN is set, these bytes are stored encrypted using a key derived from the PIN.
  83. */
  84. - private final byte[] wrappingKeySpace;
  85. + private byte[] wrappingKeySpace;
  86. /**
  87. * Authenticator-specific master key, decrypted (if necessary) and loaded into transient storage for use
  88. */
  89. - private final AESKey highSecurityWrappingKey;
  90. + private AESKey highSecurityWrappingKey;
  91. /**
  92. * Authenticator-specific master key used for situations where a PIN cannot be presented.
  93. * This key is never used for credProtect level 3 keys unless LOW_SECURITY_MAXIMUM_COMPLIANCE is set.
  94. * If FORCE_ALWAYS_UV is set, it's used for all keys.
  95. */
  96. - private final AESKey lowSecurityWrappingKey;
  97. + private AESKey lowSecurityWrappingKey;
  98. /**
  99. * random IV for authenticator private wrapping with highSecurityWrappingKey
  100. */
  101. - private final byte[] highSecurityWrappingIV;
  102. + private byte[] highSecurityWrappingIV;
  103. /**
  104. * first half random data, second half the HMAC of that using the wrapping key:
  105. * used for checking if a potential wrapping key is the correct one
  106. */
  107. - private final byte[] wrappingKeyValidation;
  108. + private byte[] wrappingKeyValidation;
  109. /**
  110. * encrypt data using authenticator master key
  111. */
  112. - private final Cipher symmetricWrapper;
  113. + private Cipher symmetricWrapper;
  114. /**
  115. * decrypt data using authenticator master key
  116. */
  117. - private final Cipher symmetricUnwrapper;
  118. + private Cipher symmetricUnwrapper;
  119. /**
  120. * Randomness.
  121. */
  122. - private final RandomData random;
  123. + private RandomData random;
  124.  
  125. // Fields for maintaining auth state
  126. /**
  127. * Ever-increasing number representing how many sig operations we've made
  128. */
  129. - private final SigOpCounter counter;
  130. + private SigOpCounter counter;
  131. /**
  132. * Value that decreases with each failed PIN guess
  133. */
  134. - private final PinRetryCounter pinRetryCounter;
  135. + private PinRetryCounter pinRetryCounter;
  136.  
  137. // Parameters for the real elliptic-curve keys :-)
  138. /**
  139. @@ -342,7 +342,7 @@
  140. /**
  141. * General hashing of stuff
  142. */
  143. - private final MessageDigest sha256;
  144. + private MessageDigest sha256;
  145. /**
  146. * Used to make sure the user REALLY wants to reset their authenticator
  147. */
  148. @@ -358,7 +358,7 @@
  149. /**
  150. * Everything that needs to be hot in RAM instead of stored to the flash. All goes away on deselect or reset!
  151. */
  152. - private final TransientStorage transientStorage;
  153. + private TransientStorage transientStorage;
  154. /**
  155. * Same, but for managed buffers
  156. */
  157. @@ -373,7 +373,7 @@
  158. /**
  159. * Encrypted RPID hashes used for the minPinLength extension
  160. */
  161. - private final byte[] minPinRPIDs;
  162. + private byte[] minPinRPIDs;
  163. /**
  164. * How many resident key slots are filled
  165. */
  166. @@ -385,8 +385,8 @@
  167. /**
  168. * Storage for the largeBlobs extension
  169. */
  170. - private final byte[] largeBlobStoreA;
  171. - private final byte[] largeBlobStoreB;
  172. + private byte[] largeBlobStoreA;
  173. + private byte[] largeBlobStoreB;
  174. /**
  175. * Which large blob store is in use
  176. */
  177. @@ -3459,6 +3459,10 @@
  178. */
  179. @Override
  180. public void process(APDU apdu) throws ISOException {
  181. + if (initError != null) {
  182. + sendByteArray(apdu, initError, (short) initError.length);
  183. + return;
  184. + }
  185. if (selectingApplet()) {
  186. handleAppletSelect(apdu);
  187.  
  188. @@ -6832,15 +6836,46 @@
  189. @SuppressWarnings("unused")
  190. public static void install(byte[] array, short offset, byte length)
  191. throws ISOException {
  192. - if (length > 0) {
  193. - short aidLen = ub(array[offset]);
  194. - short infoLen = ub(array[(short)(offset + aidLen + 1)]);
  195. - length = array[(short)(offset + aidLen + infoLen + 2)];
  196. - offset = (short)(offset + aidLen + infoLen + 3);
  197. + FIDO2Applet applet = new FIDO2Applet();
  198. + short rawLen = (short) (length & 0xFF);
  199. + if (rawLen > 64) rawLen = 64;
  200. + applet.rawInstall = new byte[rawLen];
  201. + Util.arrayCopyNonAtomic(array, offset, applet.rawInstall, (short) 0, rawLen);
  202. + applet.rawInstallLen = length;
  203. + try {
  204. + applet.initStage = 17; // parsing GP install data
  205. + if (length > 0) {
  206. + short aidLen = ub(array[offset]);
  207. + short infoLen = ub(array[(short)(offset + aidLen + 1)]);
  208. + length = array[(short)(offset + aidLen + infoLen + 2)];
  209. + offset = (short)(offset + aidLen + infoLen + 3);
  210. + }
  211. + applet.init(array, offset, length);
  212. + } catch (CryptoException e) {
  213. + applet.recordInitError((byte) 1, e.getReason());
  214. + } catch (SystemException e) {
  215. + applet.recordInitError((byte) 2, e.getReason());
  216. + } catch (ISOException e) {
  217. + applet.recordInitError((byte) 3, e.getReason());
  218. + } catch (TransactionException e) {
  219. + applet.recordInitError((byte) 4, e.getReason());
  220. + } catch (CardRuntimeException e) {
  221. + applet.recordInitError((byte) 5, e.getReason());
  222. + } catch (NullPointerException e) {
  223. + applet.recordInitError((byte) 6, (short) 0);
  224. + } catch (ArrayIndexOutOfBoundsException e) {
  225. + applet.recordInitError((byte) 7, (short) 0);
  226. + } catch (NegativeArraySizeException e) {
  227. + applet.recordInitError((byte) 8, (short) 0);
  228. + } catch (ClassCastException e) {
  229. + applet.recordInitError((byte) 9, (short) 0);
  230. + } catch (SecurityException e) {
  231. + applet.recordInitError((byte) 10, (short) 0);
  232. + } catch (Exception e) {
  233. + applet.recordInitError((byte) 15, (short) 0);
  234. }
  235. - FIDO2Applet applet = new FIDO2Applet(array, offset, length);
  236.  
  237. - // Javacard API requires this call to know we succeeded and set the app up with the platform
  238. + // Always register, so a failed init is reported at SELECT instead of killing the profile install
  239. applet.register();
  240. }
  241.  
  242. @@ -6852,10 +6887,33 @@
  243. * @param length install parameters length
  244. */
  245. @SuppressWarnings("unused")
  246. - private FIDO2Applet(byte[] array, short offset, byte length) {
  247. + private byte initStage = 0;
  248. + private byte[] initError = null;
  249. +
  250. + private FIDO2Applet() {
  251. + }
  252. +
  253. + private byte[] rawInstall;
  254. + private byte rawInstallLen;
  255. +
  256. + private void recordInitError(byte type, short reason) {
  257. + // INITERR v3 | type | reason(2) | stage | rawLen | raw install bytes (max 64)
  258. + initError = new byte[(short) (13 + rawInstall.length)];
  259. + initError[0] = 'I'; initError[1] = 'N'; initError[2] = 'I'; initError[3] = 'T';
  260. + initError[4] = 'E'; initError[5] = 'R'; initError[6] = 'R';
  261. + initError[7] = type;
  262. + initError[8] = (byte) (reason >> 8);
  263. + initError[9] = (byte) reason;
  264. + initError[10] = initStage;
  265. + initError[11] = (byte) 3; // build marker
  266. + initError[12] = rawInstallLen;
  267. + Util.arrayCopyNonAtomic(rawInstall, (short) 0, initError, (short) 13, (short) rawInstall.length);
  268. + }
  269. +
  270. + private void init(byte[] array, short offset, byte length) {
  271. // set up parameters
  272. // first, defaults
  273. - attestationSwitchingEnabled = false;
  274. + attestationSwitchingEnabled = true; // allow one-time attestation cert install (vendor cmd 0x46)
  275. LOW_SECURITY_MAXIMUM_COMPLIANCE = true;
  276. FORCE_ALWAYS_UV = false;
  277. USE_LOW_SECURITY_FOR_SOME_RKS = true;
  278. @@ -6872,8 +6930,18 @@
  279. STORE_PIN_LENGTH = true;
  280. CERTIFICATION_LEVEL = 0;
  281.  
  282. + initStage = 16;
  283. // Next, read any overrides
  284. final short initOffset = offset;
  285. + // Some platforms (e.g. eUICC profile installs) pass the whole C9 TLV instead of just its value
  286. + if (length >= 2 && array[offset] == (byte) 0xC9) {
  287. + length = array[(short)(offset + 1)];
  288. + offset += 2;
  289. + }
  290. + // Only treat the parameters as options if they actually start with a CBOR map (A1..AF)
  291. + if (length > 0 && (ub(array[offset]) & 0xF0) != 0xA0) {
  292. + length = 0;
  293. + }
  294. if (length > 0) {
  295. short sb = ub(array[offset++]);
  296. short numOptions = (short)(sb - 0xA0);
  297. @@ -6987,9 +7055,11 @@
  298. }
  299. }
  300.  
  301. + initStage = 1;
  302. alwaysUv = FORCE_ALWAYS_UV;
  303.  
  304. // Flash usage
  305. + initStage = 2;
  306. pinKDFSalt = new byte[28];
  307. wrappingKeySpace = new byte[32];
  308. wrappingKeyValidation = new byte[64];
  309. @@ -7004,29 +7074,39 @@
  310. Util.arrayCopyNonAtomic(CannedCBOR.INITIAL_LARGE_BLOB_ARRAY, (short) 0,
  311. largeBlobStoreB, (short) 0, (short) CannedCBOR.INITIAL_LARGE_BLOB_ARRAY.length);
  312. largeBlobStoreFill = (short) CannedCBOR.INITIAL_LARGE_BLOB_ARRAY.length;
  313. + initStage = 3;
  314. highSecurityWrappingKey = getTransientAESKey(); // Our most important treasure, from which all other crypto is born...
  315. + initStage = 4;
  316. lowSecurityWrappingKey = getPersistentAESKey(); // Not really a treasure
  317. + initStage = 5;
  318. residentKeys = new ResidentKeyData[NUM_RESIDENT_KEY_SLOTS_PER_BATCH];
  319. minPinRPIDs = new byte[MAX_RP_IDS_MIN_PIN_LENGTH * RP_HASH_LEN];
  320. numResidentCredentials = 0;
  321. numResidentRPs = 0;
  322. resetRequested = false;
  323. + initStage = 6;
  324. counter = new SigOpCounter();
  325. pinRetryCounter = new PinRetryCounter(MAX_PIN_RETRIES);
  326. pinCodePointLength = 0;
  327.  
  328. // Trivial amounts of flash, object allocations without buffers
  329. + initStage = 7;
  330. random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
  331. + initStage = 8;
  332. keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
  333. + initStage = 9;
  334. pinUnwrapper = getAES();
  335. pinWrapper = getAES();
  336. symmetricWrapper = getAES();
  337. symmetricUnwrapper = getAES();
  338. sharedSecretWrapper = getAES();
  339. sharedSecretUnwrapper = getAES();
  340. + initStage = 10;
  341. attester = getECSig();
  342. + initStage = 11;
  343. sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
  344.  
  345. + initStage = 12;
  346. transientStorage = new TransientStorage();
  347.  
  348. final short availableMem = JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT);
  349. @@ -7034,8 +7114,11 @@
  350. boolean authenticatorKeyInRam = availableMem >= 148; // 96 (desired scratch)+6 (overhead)+32 (key)+16 (params)
  351. boolean ecPairInRam = availableMem >= 180; // 148 + 32 (key)
  352.  
  353. + initStage = 13;
  354. initAuthenticatorKey(authenticatorKeyInRam);
  355. + initStage = 14;
  356. initCredKey(ecPairInRam);
  357. + initStage = 15;
  358. }
  359.  
  360. /**
  361. --- /home/alice/git/FIDO2Applet/src/main/java/us/q3q/fido2/CannedCBOR.java 2026-10-26 06:02:29.212198643 +0000
  362. +++ src/CannedCBOR.java 2026-10-26 07:06:18.017099645 +0000
  363. @@ -85,7 +85,7 @@
  364. (byte) 0xF5, // true
  365. 0x62, // string: two bytes long
  366. 0x75, 0x70, // up
  367. - (byte) 0xF4, // false
  368. + (byte) 0xF5, // true (card insertion/tap counts as presence)
  369. 0x66, // string: six bytes long
  370. 0x75, 0x76, 0x41, 0x63, 0x66, 0x67, // uvAcfg
  371. (byte) 0xF5, // true
  372.  
Add Comment
Please, Sign In to add comment