{"id":18015213,"url":"https://github.com/jchambers/java-noise","last_synced_at":"2026-04-26T23:31:44.451Z","repository":{"id":223016600,"uuid":"757453136","full_name":"jchambers/java-noise","owner":"jchambers","description":"A Java implementation of the Noise Protocol Framework","archived":false,"fork":false,"pushed_at":"2025-04-02T02:35:31.000Z","size":1395,"stargazers_count":0,"open_issues_count":3,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T03:29:17.404Z","etag":null,"topics":["cryptography","java","noise","noise-protocol","noise-protocol-framework","security"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jchambers.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-14T14:26:37.000Z","updated_at":"2024-09-07T12:56:11.000Z","dependencies_parsed_at":"2024-02-24T22:32:14.038Z","dependency_job_id":"db018a12-83f7-4acd-b63b-2f0844cfa5bb","html_url":"https://github.com/jchambers/java-noise","commit_stats":null,"previous_names":["jchambers/java-noise"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jchambers/java-noise","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jchambers%2Fjava-noise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jchambers%2Fjava-noise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jchambers%2Fjava-noise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jchambers%2Fjava-noise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jchambers","download_url":"https://codeload.github.com/jchambers/java-noise/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jchambers%2Fjava-noise/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32317163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"ssl_error","status_checked_at":"2026-04-26T23:26:25.802Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptography","java","noise","noise-protocol","noise-protocol-framework","security"],"created_at":"2024-10-30T04:12:51.923Z","updated_at":"2026-04-26T23:31:44.430Z","avatar_url":"https://github.com/jchambers.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# java-noise\n\njava-noise is a Java implementation of the [Noise Protocol Framework](https://noiseprotocol.org/), which describes itself as:\n\n\u003e …a framework for building crypto protocols. Noise protocols support mutual and optional authentication, identity hiding, forward secrecy, zero round-trip encryption, and other advanced features.\n\njava-noise supports all handshake patterns, handshake pattern modifiers, and cryptographic algorithms enumerated in revision 34 of the [Noise Protocol Framework specification](https://noiseprotocol.org/noise.html). Some cryptographic algorithms depend on the presence of a security provider that provides an implementation of the named algorithm. In particular:\n\n- The \"25519\" key agreement algorithm requires that the JVM have a security provider that supports the \"X25519\" `KeyAgreement` and `KeyPairGenerator` algorithms\n- The \"448\" key agreement algorithm requires that the JVM have a security provider that supports the \"X448\" `KeyAgreement` and `KeyPairGenerator` algorithms\n- The \"ChaChaPoly\" cipher algorithm requires that the JVM have a security provider that supports the \"ChaCha20-Poly1305\" `Cipher` algorithm\n- The \"SHA512\" hash algorithm requires that the JVM have a security provider that supports the \"SHA-512\" `MessageDigest` algorithm and the \"HmacSHA512\" `Mac` algorithm\n\nAll other algorithms named in the Noise Protocol Framework specification are supported unconditionally.\n\n## Reference\n\n- [java-noise API documentation (Javadoc)](https://jchambers.github.io/java-noise/apidocs/latest/)\n- [The Noise Protocol Framework specification](https://noiseprotocol.org/noise.html)\n\n## Handshakes\n\nA Noise protocol begins with a handshake in which two parties (an initiator and a responder) exchange handshake messages that contain key material and optional payloads to negotiate a shared secret key and establish an ongoing session for Noise transport messages. Noise handshakes are described by [\"handshake patterns\"](https://noiseprotocol.org/noise.html#handshake-patterns), which prescribe the handshake messages exchanged between the initiator and responder. In java-noise, Noise handshakes are managed by `NoiseHandshake` instances.\n\n`NoiseHandshake` instances can be constructed using either a `NoiseHandshakeBuilder`, which provides static initializers for common Noise handshake patterns, or a `NamedProtocolHandshakeBuilder`, which allows for arbitrary handshake pattern names, but only offers runtime checks (as opposed to compile-time checks) that appropriate key material has been provided before building a `NoiseHandshake` instance.\n\n### Interactive patterns\n\nIn the most common case, Noise handshakes implement an interactive pattern in which both parties will send and receive messages to one another once the handshake is complete. As an example, the NN interactive handshake pattern is defined as:\n\n```\nNN:\n  -\u003e e\n  \u003c- e, ee\n```\n\nThe parties in an NN handshake exchange messages until all required messages have been exchanged, then the handshake instances yield interactive transport instances:\n\n```java\nfinal NoiseHandshake initiatorHandshake = NoiseHandshakeBuilder.forNNInitiator()\n    .setComponentsFromProtocolName(\"Noise_NN_25519_AESGCM_SHA256\")\n    .build();\n\nfinal NoiseHandshake responderHandshake = NoiseHandshakeBuilder.forNNResponder()\n    .setComponentsFromProtocolName(\"Noise_NN_25519_AESGCM_SHA256\")\n    .build();\n\n// -\u003e e (with an empty payload)\nfinal byte[] initiatorEMessage = initiatorHandshake.writeMessage((byte[]) null);\nresponderHandshake.readMessage(initiatorEMessage);\n\n// \u003c- e, ee (with an empty payload)\nfinal byte[] responderEEeMessage = responderHandshake.writeMessage((byte[]) null);\ninitiatorHandshake.readMessage(responderEEeMessage);\n\nassert initiatorHandshake.isDone();\nassert responderHandshake.isDone();\n\nfinal NoiseTransport initiatorTransport = initiatorHandshake.toTransport();\nfinal NoiseTransport responderTransport = responderHandshake.toTransport();\n```\n\n### One-way patterns\n\nNoise handshakes may also use one-way patterns. As the Noise Protocol Framework specification notes:\n\n\u003e These patterns could be used to encrypt files, database records, or other non-interactive data streams.\n\nOne-way handshakes exchange handshake messages in the same way as interactive handshakes, but instead of producing interactive `NoiseTransport` instances, one-way handshakes produce a one-way `NoiseTransportWriter` for initiators or `NoiseTransportReader` for responders. As an example, the N handshake pattern is defined as:\n\n```\nN:\n  \u003c- s\n  ...\n  -\u003e e, es\n```\n\nThe parties in an N handshake exchange messages as usual, then the handshake instances yield one-way transport instances:\n\n```java\nfinal NoiseHandshake initiatorHandshake = NoiseHandshakeBuilder.forNInitiator(responderStaticPublicKey)\n    .setComponentsFromProtocolName(\"Noise_N_25519_AESGCM_SHA256\")\n    .build();\n\nfinal NoiseHandshake responderHandshake = NoiseHandshakeBuilder.forNResponder(responderStaticKeyPair)\n    .setComponentsFromProtocolName(\"Noise_N_25519_AESGCM_SHA256\")\n    .build();\n\n// -\u003e e, es (with an empty payload)\nfinal byte[] initiatorEphemeralKeyMessage = initiatorHandshake.writeMessage((byte[]) null);\nresponderHandshake.readMessage(initiatorEphemeralKeyMessage);\n\nassert initiatorHandshake.isDone();\nassert responderHandshake.isDone();\n\nfinal NoiseTransportWriter transportWriter = initiatorHandshake.toTransportWriter();\nfinal NoiseTransportReader transportReader = responderHandshake.toTransportReader();\n```\n\n### Fallback patterns\n\nNoise handshakes can \"fall back\" to another pattern to handle certain kinds of errors. As an example, the [Noise Pipes](https://noiseprotocol.org/noise.html#noise-pipes) compound protocol expects that initiators will usually have the responder's static public key available from a previous \"full\" (XX) handshake, and can use an abbreviated (IK) handshake pattern with that static key set via a pre-handshake message. If the responder can't decrypt a message from the initiator, though, it might conclude that the initiator has a stale copy of its public key and can fall back to a \"full\" (XXfallback) handshake.\n\nThe IK handshake pattern is defined as:\n\n```\nIK:\n  \u003c- s\n  ...\n  -\u003e e, es, s, ss\n  \u003c- e, ee, se\n```\n\n…and the XXfallback pattern is defined as:\n\n```\nXXfallback:\n  -\u003e e\n  ...\n  \u003c- e, ee, s, es\n  -\u003e s, se\n```\n\nAs an example, consider a scenario where the initiator of an IK handshake has a \"stale\" static key for the responder:\n\n```java\nfinal NoiseHandshake ikInitiatorHandshake =\n    NoiseHandshakeBuilder.forIKInitiator(initiatorStaticKeyPair, staleRemoteStaticPublicKey)\n        .setComponentsFromProtocolName(\"Noise_IK_25519_AESGCM_SHA256\")\n        .build();\n\nfinal NoiseHandshake ikResponderHandshake =\n    NoiseHandshakeBuilder.forIKResponder(currentResponderStaticKeyPair)\n        .setComponentsFromProtocolName(\"Noise_IK_25519_AESGCM_SHA256\")\n        .build();\n```\n\nThe initiator sends its first message to the responder, which won't be able to decrypt the message due to the static key disagreement:\n\n```java\n// -\u003e e, es, s, ss (with an empty payload)\nfinal byte[] initiatorStaticKeyMessage = ikInitiatorHandshake.writeMessage((byte[]) null);\n\n// Throws an AEADBadTagException because the initiator has a stale static key for the responder\nikResponderHandshake.readMessage(initiatorStaticKeyMessage);\n```\n\nRather than simply failing the handshake (assuming both the initiator and responder are expecting that a fallback may happen), the responder can fall back to the XXfallback pattern, reusing the ephemeral key it already received from the initiator as a pre-handshake message, and write a message to continue the XXfallback pattern:\n\n```java\nfinal NoiseHandshake xxFallbackResponderHandshake =\n    ikResponderHandshake.fallbackTo(\"XXfallback\");\n\n// \u003c- e, ee, s, es (with an empty payload)\nfinal byte[] responderStaticKeyMessage = xxFallbackResponderHandshake.writeMessage((byte[]) null);\n```\n\nThe initiator will fail to decrypt the message from the responder:\n\n```java\n// Throws an AEADBadTagException\nikInitiatorHandshake.readMessage(responderStaticKeyMessage);\n```\n\nLike the responder, the initiator can take the decryption failure as a cue to fall back to the XXfallback pattern, then read the message and finish the handshake:\n\n```java\nfinal NoiseHandshake xxFallbackInitiatorHandshake =\n    ikInitiatorHandshake.fallbackTo(\"XXfallback\");\n\nxxFallbackInitiatorHandshake.readMessage(responderStaticKeyMessage);\n\nfinal byte[] initiatorFallbackStaticKeyMessage =\n    xxFallbackInitiatorHandshake.writeMessage((byte[]) null);\n\nxxFallbackResponderHandshake.readMessage(initiatorFallbackStaticKeyMessage);\n\nassert xxFallbackInitiatorHandshake.isDone();\nassert xxFallbackResponderHandshake.isDone();\n```\n\nOnce the handshake is finished, the transition to the transport phase of the protocol continues as usual.\n\n## Transports\n\nOnce the handshake phase of a Noise protocol has finished, `NoiseHandshake` instances may be transformed or \"split\" (in the terminology of the Noise Protocol Framework specification) into Noise transport objects. Depending on the nature of the handshake and the role of the party in the handshake, a `NoiseHandshake` instance may be transformed into exactly one of:\n\n- A `NoiseTransportWriter` if the handshake is a one-way handshake for the handshake initiator\n- A `NoiseTransportReader` if the handshake is a one-way handshake for the handshake responder\n- A `NoiseTransport` if the handshake is interactive\n\nOnce a handshake has been split, a Noise transport instance can be used to exchange transport messages as needed. Note that unlike handshake messages, transport messages contain only payload ciphertexts (i.e. they do not contain key material, and their content is always encrypted). As an example starting from a finished interactive handshake:\n\n```java\nfinal NoiseTransport initiatorTransport = initiatorHandshake.toTransport();\nfinal NoiseTransport responderTransport = responderHandshake.toTransport();\n\nfinal byte[] originalPlaintextBytes = \"Hello, Bob!\".getBytes(StandardCharsets.UTF_8);\n\nfinal byte[] aliceToBobCiphertext =\n    initiatorTransport.writeMessage(originalPlaintextBytes);\n\nassert !Arrays.equals(aliceToBobCiphertext, originalPlaintextBytes);\n\nfinal byte[] aliceToBobPlaintext = responderTransport.readMessage(aliceToBobCiphertext);\n\nassert Arrays.equals(aliceToBobPlaintext, originalPlaintextBytes);\n```\n\n## Limitations and cut corners\n\njava-noise strives to be well-behaved, but its implementation makes a few minor deviations from pedantic correctness. In particular:\n\n- [IETF RFC 7693 (\"The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)\")](https://datatracker.ietf.org/doc/html/rfc7693), [section 2.1](https://datatracker.ietf.org/doc/html/rfc7693#section-2.1) specifies that BLAKE2b must support up to $2^{128}$ bytes of input data. The implementation included in java-noise only supports up to $2^{64}$. To put that limitation into perspective, though, BLAKE2b can hash about 1GiB/sec of input data on modern hardware. At that rate, it would take nearly 300 years of continuous work on a single input stream to encounter that limit. On top of that, the [Noise Protocol Framework specification, section 3](https://noiseprotocol.org/noise.html#message-format) specifies that Noise messages may not be more than 64KiB, and so this limitation is very unlikely to matter in any practical scenario.\n- Similarly, the [Noise Protocol Framework specification, section 5](https://noiseprotocol.org/noise.html#processing-rules) specifies that `CipherState` objects (practically exposed as `NoiseHandshake` and the `NoiseTransport` family of interfaces in java-noise) must fail and self-delete in the event of nonce rollover. java-noise does _not_ implement this check because the nonce is a 64-bit value, and assuming a transfer rate of 1,000,000 messages per second, encountering this limit would take nearly 300,000 years on modern hardware.\n- In the interest of avoiding \"nuisance\" exceptions (for example, declaring that `NoiseTransport#writeMessage` might throw a `NoSuchAlgorithmException`), java-noise assumes that if an implementation of a cryptographic algorithm can be instantiated once, it can be re-instantiated again later. In theory, this assumption may not always hold; an implementation of a cryptographic algorithm may be provided by a Java security `Provider` that is later removed. This situation is unlikely in practice (and callers operating in an environment where they do not control their security infrastructure likely have bigger problems), but if a previously-available algorithm becomes unavailable, various components of java-noise may throw an unchecked `AssertionError` instead of a `NoSuchAlgorithmException`.\n\n## Test vectors\n\nTest vectors for this project come from several sources:\n\n1. java-noise uses the test vectors from the [cacophony](https://github.com/haskell-cryptography/cacophony) project without significant modification\n2. java-noise uses parts of the \"fallback\" test vectors from the [noise-c](https://github.com/rweather/noise-c) project, but without the PSK-related fallback tests, since noise-c's PSK implementation appears to adhere to an earlier version of the Noise specification\n3. Test vectors for the BLAKE2 algorithms come from the [BLAKE2](https://www.blake2.net/) project\n\nThe general idea behind Noise test vectors is [explained on the Noise wiki](https://github.com/noiseprotocol/noise_wiki/wiki/Test-vectors), though most publicly available test vectors seem to deviate from the format described on the wiki to some degree.\n\n## License and status\n\njava-noise is published under the [MIT License](https://opensource.org/licenses/MIT).\n\njava-noise has not yet been published to any artifact repository, but likely will be in the future following a self-imposed \"cooling down\" period. java-noise passes tests from a variety of sources, but has not received an independent security audit of any kind.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjchambers%2Fjava-noise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjchambers%2Fjava-noise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjchambers%2Fjava-noise/lists"}