Add com.codename1.security crypto API + OtpField widget#4994
Merged
Conversation
Introduces a first-party cryptography API in com.codename1.security so apps
no longer need an external cn1lib for routine hashing, MAC, encryption,
signing, JWT, and OTP work. Pure-Java algorithms produce identical output
on every platform; AES/RSA/Signature/SecureRandom route through each port's
native crypto provider.
New public API:
- Hash MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 (pure Java)
- Hmac RFC 2104 + constant-time compare (pure Java)
- SecureRandom platform CSPRNG with bias-free intBelow/longBelow
- Cipher AES (GCM, CBC, ECB) and RSA (OAEP, PKCS#1)
- Signature RSA and ECDSA signing/verification
- KeyGenerator AES/HMAC keys, RSA key pairs
- Jwt JWT sign/verify for HS, RS, ES families (RFC 7519)
- Otp HOTP (RFC 4226) + TOTP (RFC 6238), authenticator-compatible
- Base32 / Base64Url, CryptoException
UI: com.codename1.components.OtpField -- segmented OTP input with
auto-advance, backspace stepping, paste distribution, and a complete
listener.
Impl layer:
- CodenameOneImplementation gains aesEncrypt/Decrypt, rsaEncrypt/Decrypt,
cryptoSign/Verify, secureRandomBytes, generateRsaKeyPair,
generateSymmetricKey. Default implementation uses java.security via
reflection so JavaSE simulator and Android work out of the box.
- com.codename1.io.Util exposes narrow public delegates the security
package uses; getImplementation() stays package-private.
iOS port:
- New Ports/iOSPort/nativeSources/CN1Crypto.{h,m} backs AES-CBC/GCM, RSA
OAEP/PKCS#1, RSA/ECDSA sign+verify, RSA keypair generation, and secure
random against Apple's Security framework + CommonCrypto.
- IOSNative + IOSImplementation override every crypto bridge method to
route through CN1Crypto.
Tests: 32 new tests under maven/core-unittests with RFC reference vectors
(FIPS 180-4 for hashes, RFC 4231 for HMAC, RFC 4226/6238 for OTP), plus
AES-GCM tamper detection, RSA OAEP round-trip, RSA sign/verify with
tampering, and a JWT RS256 round-trip.
Developer guide: new "Cryptographic primitives" section in
docs/developer-guide/security.asciidoc with examples for every entry
point, an algorithm-constant reference table, and recommended-key-size
guidance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apps that don't reference com.codename1.security.* now have none of the
CommonCrypto / Security framework encryption symbols in their iOS binary
-- and in particular, the AES-GCM SPI symbols (CCCryptorGCMAddIV etc.)
stay completely out unless the app opts in.
CN1Crypto.h: declare placeholder #define toggles
CN1_INCLUDE_CRYPTO
CN1_INCLUDE_CRYPTO_GCM
(commented-out by default; flipped by IPhoneBuilder when relevant)
CN1Crypto.m / IOSNative.m crypto block:
- All implementations wrapped in #ifdef CN1_INCLUDE_CRYPTO. When the
define is absent we emit no-op stubs that always return
CN1_CRYPTO_E_UNSUPPORTED so the IOSImplementation overrides have
something to link against but no encryption symbols are referenced.
- The AES-GCM SPI externs and cn1_crypto_aes_gcm body are nested
inside a separate #ifdef CN1_INCLUDE_CRYPTO_GCM so even apps using
AES-CBC / RSA / signatures don't pull in the private GCM symbols
unless they ask for it.
IPhoneBuilder:
- Adds usesCryptoAPI / usesCryptoGcm flags. usesCryptoAPI is set by
scanClassesForPermissions when any class in com/codename1/security/
is referenced; usesCryptoGcm is opt-in via the ios.crypto.gcm
build hint (default false).
- Flips the CN1Crypto.h placeholders to the active defines when
needed.
- When the crypto API is used, injects
<key>ITSAppUsesNonExemptEncryption</key><false/> into Info.plist so
App Store Connect doesn't re-prompt on every upload. We always
route through Apple-provided crypto (and, for GCM opt-in, stable
SPI symbols backed by libcommonCrypto), which qualifies for the
EAR 740.17 standard-cryptography exemption.
- ios.appUsesNonExemptEncryption build hint overrides the default
-- pass "true" if the app links proprietary crypto on top of ours,
or "" to omit the key and answer in App Store Connect.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves conflicts with the parallel "com.codename1.security: biometric auth + secure storage" addition (#4987) that also landed in com.codename1.security. The two contributions are independent class sets that happen to share the package -- preserved both. Conflict resolution: - IOSNative.m: keep both the crypto bridge block (#ifdef CN1_INCLUDE_CRYPTO) and the biometrics + SecureStorage block back-to-back. - IPhoneBuilder.java: keep usesBiometrics + usesCryptoAPI/usesCryptoGcm fields side by side and route classes in com/codename1/security/ to the right flag by name (Biometric*, SecureStorage and AuthenticationOptions go to biometrics; everything else goes to crypto). - GenerateCN1AccessRegistry.java: extend the existing one-release INTERNAL_CN1_TYPES exclusion to cover the new crypto types too, so the playground bridge generator skips them until the cloud TeaVM backend catches up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Collaborator
Author
|
Compared 20 screenshots: 20 matched. |
Contributor
Cloudflare Preview
|
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
The previous default implementation used java.security via reflection
inside CodenameOneImplementation. The core compiles against the CLDC11
java.lang.Class stub (no getMethod / getConstructor) and the iOS port
goes through ParparVM (no java.lang.reflect.Method symbols), so the
reflection broke the Ant test-javase build, the iOS native compile,
and every transitively-failing job (build, build-ios, build-ios-metal,
native-ios, packaging, build-test 8/17/21).
Refactor:
- CodenameOneImplementation: crypto methods now throw a clear
"not supported on this platform" RuntimeException by default;
zero java.security / javax.crypto references in core.
- JavaSEPort: real implementations using direct java.security /
javax.crypto calls (full JDK is on the classpath at compile time
and at runtime in the simulator).
- AndroidImplementation: same direct-JCE overrides; Android ships
the standard JCE provider.
- TestCodenameOneImplementation (unit-test fixture in core-unittests):
same overrides so the existing 32 crypto tests still exercise the
bridge end-to-end.
- iOS port already overrides via IOSNative + CN1Crypto.{h,m}; no
change there.
Also fixes vale-linter alerts on the new developer guide section:
- "and so on" -> rewritten without the suggestion
- "it is" -> "it's"
- "very deliberately" -> "have decided"
- "auto-advancing" -> "advances to the next box"
32/32 tests pass on the JavaSE bridge (Hash, Hmac, OtpField, Jwt,
Cipher, Signature, KeyGenerator, SecureRandom round-trips).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 11 screenshots: 11 matched. |
CI's static-analysis quality gate fails the build on a set of forbidden
PMD rules. Bring the new package into compliance:
- ControlStatementBraces: add { ... } around single-statement if / else
/ for / while bodies that the previous draft wrote inline.
- MissingOverride: annotate every method that overrides one of
MessageDigestImpl's abstract / virtual hooks (reset, digest,
digestLength, update, processBlock, writeStateBigEndian) and the
Block64 base-class update overrides. Also annotate the
DataChangedListener.dataChanged inner class in OtpField.
- LiteralsFirstInComparisons: flip s.equals("X") -> "X".equals(s) in
MessageDigestImpl.create and Hmac.blockSizeFor.
- EmptyControlStatement: drop the empty if-truncated branch in
Sha512Family.digest; collapse to a single !truncated check.
- ForLoopCanBeForeach: convert the array index-loops to enhanced for
in Hash.toHex, Base32.encode and OtpField.fireCompleteIfFull.
- OneDeclarationPerLine: split chained int/long h0,h1,h2,...
declarations in the SHA inner classes onto separate lines.
- ClassNamingConventions: rename inner classes Sha2_32 -> Sha256Family
and Sha2_64 -> Sha512Family (the underscore violated the rule).
Local pmd:check on the new files now reports 0 violations from this PR.
Also pulls in the vale-asciidoc fixes for security.asciidoc that the
previous commit dropped (and that the build-docs job had already
flagged before this).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A second CI gate -- Checkstyle, also bound to the verify phase of
core-unittests -- flagged 109 brace-placement, whitespace, and
continuation-indent issues across the new files. Bring them to zero:
- LeftCurlyCheck (94 instances): split single-line `if (x) { y; }` and
single-line method definitions like
`public byte[] md5(byte[] d) { return create(MD5).digest(d); }`
into the canonical three-line form. Codenames One's checkstyle
config requires a newline immediately after the opening brace.
- WhitespaceAfterCheck (15 instances): add spaces after commas in
Hash.HEX array literal and after primitive `(long)` casts in
SecureRandom.longBelow.
- IndentationCheck (16 instances): bump SHA-512 word-mixing
continuation lines from 23 to 24 columns to match the
expected level (the `^` operator was one column shy of the
canonical four-space continuation).
Local mvn checkstyle:check + pmd:check both report 0 violations from
this PR after these changes; 32/32 security tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
… device test Code-review feedback on PR #4994: 1. Reuse the existing util.Base64 instead of adding a parallel Base64Url class. util.Base64 is already SIMD-optimized; layer URL-safe encode/decode on top of its encodeNoNewline path and inherit the speedup. - util.Base64: new encodeUrlSafe(byte[]) / decodeUrlSafe(String) that run through the same fast encoder, then map +-> / -> _ and strip trailing = padding (RFC 4648 sec5). - Jwt, JwtTest, OtpTest, package-info: switch to the new helpers. - Drop security/Base64Url.java entirely. 2. Pull the common key fields (algorithm, encoded, format) into a new abstract Key base class. PublicKey/PrivateKey/SecretKey now extend it and only carry the type-specific extras (static factories for the asymmetric pair; getBitLength on the symmetric one). 3. Otp.otpauthUri(issuer, accountName, secret, ...): builds the canonical otpauth://totp/... URI per the Google Authenticator KeyUri spec so the secret can be displayed as a QR code on enrolment screens. Six-digit / 30-second / SHA-1 default matches what every authenticator app expects. 4. Developer guide: expand the OTP section into a real two-factor-auth tutorial -- enrolment via otpauthUri + QR rendering, verification via OtpField + Otp.verifyTotp, server-side guidance, rate-limiting note. Documents three approaches for actually rendering the QR (server-side render URL, QR cn1lib, plain Base32 typed entry) since core doesn't ship a QR encoder yet. 5. CryptoApiTest: device-side coverage in scripts/hellocodenameone that mirrors the JUnit assertions (hash + HMAC RFC vectors, HOTP/TOTP RFC vectors, AES-GCM round-trip + tamper detection, RSA-OAEP round-trip, RSA-SHA-256 sign/verify, JWT HS256 + RS256 round-trips, otpauthUri shape). Registered in Cn1ssDeviceRunner; skipped on HTML5 (no crypto bridge on the JS port yet). SIMD acceleration for hashing was investigated but declined: SHA-2's round state machine has data dependencies that block single-message vectorization, and the current Simd surface doesn't expose 32/64-bit rotation. The natural SIMD beneficiary in this PR -- Base64 -- is already covered by util.Base64. Local: pmd:check, checkstyle:check both 0 violations; 34 JUnit tests pass (32 from prior commits + 2 new otpauthUri tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI's javac on JDK 8 / 17 / 21 uses the US-ASCII default file.encoding (file.encoding=UTF-8 is only the JDK 18+ default per JEP 400) and choked on the section-sign character in two new doc comments: RFC 4648 §5 Swap the symbol for the spelled-out "sec" abbreviation, mirroring the ASCII-only convention noted in the previous biometrics commit (#4987) that fixed the same family of failures for the em-dash. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The crypto bridge methods on IOSNative -- aesCbc, aesGcm, rsaEncrypt,
rsaDecrypt, sign, verify, generateRsaKeyPair -- all return an int.
ParparVM emits two C entry points for every non-void native: the
unmangled implementation (com_..._methodName___paramTypes) and a
_R_<returnType>-suffixed forwarder that the bytecode dispatcher
actually calls.
The earlier commits provided only the first half, so the previous
build-ios job (which transpiled the framework but didn't transitively
include the crypto code path) passed -- the symbols were dead-code-
eliminated. Adding CryptoApiTest to scripts/hellocodenameone now keeps
those methods alive, the linker hunts for the _R_int wrappers, and
the iOS packaging job fails with:
Undefined symbols for architecture arm64:
"_com_codename1_impl_ios_IOSNative_aesCbc___..._R_int"
"_com_codename1_impl_ios_IOSNative_aesGcm___..._R_int"
...
Forward each wrapper to the matching base implementation. The base
itself is either the real CommonCrypto-backed version (when
CN1_INCLUDE_CRYPTO is defined by IPhoneBuilder) or the
CN1_CRYPTO_E_UNSUPPORTED stub, so this single set of wrappers serves
both build configurations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
com.codename1.securitypackage (hashes, HMAC, AES, RSA, signatures, JWT, HOTP/TOTP) and acom.codename1.components.OtpFieldUI widget, so apps no longer need an external cn1lib for routine crypto work.java.securityvia reflection so the simulator and Android work out of the box.Ports/iOSPort/nativeSources/CN1Crypto.{h,m}(CommonCrypto + Security framework) called fromIOSNative+IOSImplementation.What's in the package
HashHmacSecureRandomintBelow/longBelowCipherSignatureKeyGenerator,KeyPair,SecretKey,PublicKey,PrivateKeyJwtOtpBase32,Base64Url,CryptoExceptioncom.codename1.components.OtpFieldImpl layer
CodenameOneImplementationgainsaesEncrypt/Decrypt,rsaEncrypt/Decrypt,cryptoSign/Verify,secureRandomBytes,generateRsaKeyPair,generateSymmetricKey. Default implementation does the work via reflection againstjava.security/javax.crypto.com.codename1.io.Utilexposes narrow public static delegates (Util.secureRandomBytes,Util.aesEncrypt, etc.) that the security package uses.Util.getImplementation()stays package-private — the security package never reaches for the raw impl.IOSImplementationand routes through newnativemethods onIOSNativewhose C-side implementations live in the newCN1Crypto.{h,m}files innativeSources/.Tests
32 new tests under
maven/core-unittests/src/test/java/com/codename1/security:HashTest— FIPS 180-4 reference vectors for MD5/SHA-1/SHA-256/SHA-384/SHA-512 + multi-block (1MB) coverageHmacTest— RFC 4231 vectors (cases 1, 2 SHA-256/512, case 6 long key)OtpTest— RFC 4226 appendix D and RFC 6238 appendix B vectors, plus drift/tolerance assertions and Base32 round-tripJwtTest— round-trip + canonical jwt.io HS256 example + malformed/none-alg handlingCipherSignatureTest— AES-GCM round-trip + tamper detection, AES-CBC, RSA-OAEP, RSA-SHA-256 sign/verify with tampering, JWT RS256 round-trip, SecureRandom checksDeveloper guide
New "Cryptographic primitives" section in
docs/developer-guide/security.asciidocwith runnable examples for every entry point, an algorithm-constant reference table, and recommended-key-size guidance.Test plan
mvn -pl core compile -DskipTestssucceedscd maven/core-unittests && mvn test -Dtest='HashTest,HmacTest,OtpTest,JwtTest,CipherSignatureTest'→ 32/32 passmvn -pl ios -am install -Dmaven.javadoc.skip=true -DskipTestssucceedsHash,Hmac,Otp, AES-GCM round-trip, RSA sign/verify against the new native bridgeasciidoctor docs/developer-guide/developer-guide.asciidocrenders without errors🤖 Generated with Claude Code