├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── HEADER ├── LICENSE ├── README.md ├── SECURITY.md ├── jagged-api ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ ├── DecryptingChannelFactory.java │ │ ├── EncryptingChannelFactory.java │ │ ├── FileKey.java │ │ ├── PayloadException.java │ │ ├── RecipientStanza.java │ │ ├── RecipientStanzaReader.java │ │ ├── RecipientStanzaWriter.java │ │ └── UnsupportedRecipientStanzaException.java │ └── test │ └── java │ └── com │ └── exceptionfactory │ └── jagged │ └── FileKeyTest.java ├── jagged-bech32 ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ └── bech32 │ │ ├── Bech32.java │ │ ├── Bech32Address.java │ │ ├── SharedCoder.java │ │ ├── StandardBech32Address.java │ │ ├── StandardDecoder.java │ │ └── StandardEncoder.java │ └── test │ └── java │ └── com │ └── exceptionfactory │ └── jagged │ └── bech32 │ └── Bech32Test.java ├── jagged-bom └── pom.xml ├── jagged-framework ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ └── framework │ │ ├── armor │ │ ├── ArmoredDecodingException.java │ │ ├── ArmoredDecryptingChannelFactory.java │ │ ├── ArmoredEncryptingChannelFactory.java │ │ ├── ArmoredIndicator.java │ │ ├── ArmoredReadableByteChannel.java │ │ ├── ArmoredSeparator.java │ │ └── ArmoredWritableByteChannel.java │ │ ├── codec │ │ ├── CanonicalBase64.java │ │ ├── CanonicalBase64Decoder.java │ │ ├── CanonicalBase64Encoder.java │ │ └── CanonicalBase64OutputStream.java │ │ ├── crypto │ │ ├── ByteBufferCipherFactory.java │ │ ├── ByteBufferDecryptor.java │ │ ├── ByteBufferEncryptor.java │ │ ├── CipherFactory.java │ │ ├── CipherKey.java │ │ ├── CryptographicAlgorithm.java │ │ ├── CryptographicAlgorithmKey.java │ │ ├── CryptographicKeyDescription.java │ │ ├── CryptographicKeyType.java │ │ ├── EncryptedFileKey.java │ │ ├── FileKeyDecryptor.java │ │ ├── FileKeyDecryptorFactory.java │ │ ├── FileKeyEncryptor.java │ │ ├── FileKeyEncryptorFactory.java │ │ ├── FileKeyIvParameterSpec.java │ │ ├── HashedDerivedKeyProducer.java │ │ ├── HeaderKeyProducer.java │ │ ├── HeaderKeyProducerFactory.java │ │ ├── MacKey.java │ │ ├── MessageAuthenticationCodeProducer.java │ │ ├── MessageAuthenticationCodeProducerFactory.java │ │ ├── PayloadIvParameterSpec.java │ │ ├── PayloadKeyProducer.java │ │ ├── PayloadKeyProducerFactory.java │ │ ├── PayloadNonceKey.java │ │ ├── SharedSaltKey.java │ │ ├── SharedSecretKey.java │ │ ├── StandardByteBufferCipherFactory.java │ │ ├── StandardByteBufferDecryptor.java │ │ ├── StandardByteBufferEncryptor.java │ │ ├── StandardFileKeyDecryptor.java │ │ ├── StandardFileKeyEncryptor.java │ │ ├── StandardHeaderKeyProducer.java │ │ ├── StandardMessageAuthenticationCodeProducer.java │ │ └── StandardPayloadKeyProducer.java │ │ ├── format │ │ ├── AuthenticatedStandardFileHeaderWriter.java │ │ ├── FileHeader.java │ │ ├── FileHeaderReader.java │ │ ├── FileHeaderWriter.java │ │ ├── FileKeyReader.java │ │ ├── HeaderDecodingException.java │ │ ├── PayloadKeyReader.java │ │ ├── PayloadKeyWriter.java │ │ ├── SectionIndicator.java │ │ ├── SectionSeparator.java │ │ ├── StandardFileHeader.java │ │ ├── StandardFileHeaderReader.java │ │ ├── StandardFileHeaderWriter.java │ │ ├── StandardFileKeyReader.java │ │ ├── StandardPayloadKeyReader.java │ │ ├── StandardPayloadKeyWriter.java │ │ └── StandardRecipientStanza.java │ │ └── stream │ │ ├── ChunkSize.java │ │ ├── DecryptingChannel.java │ │ ├── EncryptingChannel.java │ │ ├── StandardDecryptingChannelFactory.java │ │ └── StandardEncryptingChannelFactory.java │ └── test │ └── java │ └── com │ └── exceptionfactory │ └── jagged │ └── framework │ ├── armor │ ├── ArmoredDecryptingChannelFactoryTest.java │ ├── ArmoredEncryptingChannelFactoryTest.java │ ├── ArmoredReadableByteChannelTest.java │ └── ArmoredWritableByteChannelTest.java │ ├── codec │ ├── CanonicalBase64DecoderTest.java │ └── CanonicalBase64EncoderTest.java │ ├── crypto │ ├── CipherFactoryTest.java │ ├── CipherKeyTest.java │ ├── FileKeyIvParameterSpecTest.java │ ├── HeaderKeyProducerFactoryTest.java │ ├── MacKeyTest.java │ ├── MessageAuthenticationCodeProducerFactoryTest.java │ ├── PayloadIvParameterSpecTest.java │ ├── PayloadKeyProducerFactoryTest.java │ ├── SharedSaltKeyTest.java │ ├── SharedSecretKeyTest.java │ ├── StandardByteBufferCipherFactoryTest.java │ ├── StandardByteBufferDecryptorTest.java │ ├── StandardByteBufferEncryptorTest.java │ ├── StandardFileKeyDecryptorTest.java │ ├── StandardFileKeyEncryptorTest.java │ ├── StandardHeaderKeyProducerTest.java │ ├── StandardMessageAuthenticationCodeProducerTest.java │ └── StandardPayloadKeyProducerTest.java │ ├── format │ ├── StandardFileHeaderReaderTest.java │ ├── StandardFileKeyHeaderTest.java │ ├── StandardPayloadKeyReaderTest.java │ └── StandardPayloadKeyWriterTest.java │ └── stream │ ├── DecryptingChannelTest.java │ ├── EncryptingChannelTest.java │ ├── StandardDecryptingChannelFactoryTest.java │ └── StandardEncryptingChannelFactoryTest.java ├── jagged-scrypt ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ └── scrypt │ │ ├── DerivedWrapKeyProducer.java │ │ ├── PasswordBasedKeyDerivationFunction2.java │ │ ├── RecipientIndicator.java │ │ ├── Salsa20RoundReducedFunction.java │ │ ├── ScryptDerivedWrapKeyProducer.java │ │ ├── ScryptFunction.java │ │ ├── ScryptRecipientStanza.java │ │ ├── ScryptRecipientStanzaReader.java │ │ ├── ScryptRecipientStanzaReaderFactory.java │ │ ├── ScryptRecipientStanzaWriter.java │ │ └── ScryptRecipientStanzaWriterFactory.java │ └── test │ └── java │ └── com │ └── exceptionfactory │ └── jagged │ └── scrypt │ ├── PasswordBasedKeyDerivationFunction2Test.java │ ├── Salsa20RoundReducedFunctionTest.java │ ├── ScryptDerivedWrapKeyProducerTest.java │ ├── ScryptFunctionTest.java │ ├── ScryptRecipientStanzaReaderFactoryTest.java │ ├── ScryptRecipientStanzaReaderTest.java │ ├── ScryptRecipientStanzaWriterFactoryTest.java │ └── ScryptRecipientStanzaWriterTest.java ├── jagged-ssh ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ └── ssh │ │ ├── Ed25519KeyConverter.java │ │ ├── Ed25519KeyIndicator.java │ │ ├── Ed25519PrivateKey.java │ │ ├── Ed25519PublicKey.java │ │ ├── EllipticCurveKeyType.java │ │ ├── EmptyInputKey.java │ │ ├── KeyPairReader.java │ │ ├── KeySeparator.java │ │ ├── OpenSshKeyByteBufferReader.java │ │ ├── OpenSshKeyIndicator.java │ │ ├── OpenSshKeyPairReader.java │ │ ├── PublicKeyFingerprintProducer.java │ │ ├── PublicKeyMarshaller.java │ │ ├── PublicKeyReader.java │ │ ├── RsaOaepCipherFactory.java │ │ ├── RsaPublicKeyFactory.java │ │ ├── SharedSecretKeyProducer.java │ │ ├── SharedWrapKeyProducer.java │ │ ├── SshEd25519DerivedKey.java │ │ ├── SshEd25519KeyType.java │ │ ├── SshEd25519MarshalledKey.java │ │ ├── SshEd25519OpenSshKeyPairReader.java │ │ ├── SshEd25519PublicKeyMarshaller.java │ │ ├── SshEd25519PublicKeyReader.java │ │ ├── SshEd25519RecipientIndicator.java │ │ ├── SshEd25519RecipientStanza.java │ │ ├── SshEd25519RecipientStanzaReader.java │ │ ├── SshEd25519RecipientStanzaReaderFactory.java │ │ ├── SshEd25519RecipientStanzaWriter.java │ │ ├── SshEd25519RecipientStanzaWriterFactory.java │ │ ├── SshEd25519SharedWrapKeyProducer.java │ │ ├── SshKeyType.java │ │ ├── SshPublicKeyReader.java │ │ ├── SshRsaOpenSshKeyPairReader.java │ │ ├── SshRsaPublicKeyMarshaller.java │ │ ├── SshRsaPublicKeyReader.java │ │ ├── SshRsaRecipientIndicator.java │ │ ├── SshRsaRecipientStanza.java │ │ ├── SshRsaRecipientStanzaReader.java │ │ ├── SshRsaRecipientStanzaReaderFactory.java │ │ ├── SshRsaRecipientStanzaWriter.java │ │ ├── SshRsaRecipientStanzaWriterFactory.java │ │ ├── StandardEd25519KeyConverter.java │ │ ├── StandardPublicKeyFingerprintProducer.java │ │ ├── StandardRsaPublicKeyFactory.java │ │ ├── X25519BasePointPublicKey.java │ │ ├── X25519KeyAgreementFactory.java │ │ ├── X25519KeyPairGeneratorFactory.java │ │ ├── X25519SharedSecretKeyProducer.java │ │ └── X25519SharedWrapKeyProducer.java │ └── test │ └── java │ └── com │ └── exceptionfactory │ └── jagged │ └── ssh │ ├── Ed25519KeyPairProvider.java │ ├── OpenSshKeyPairReaderTest.java │ ├── RsaKeyPairProvider.java │ ├── SshEd25519OpenSshKeyPairReaderTest.java │ ├── SshEd25519PublicKeyMarshallerTest.java │ ├── SshEd25519PublicKeyReaderTest.java │ ├── SshEd25519RecipientStanzaReaderFactoryTest.java │ ├── SshEd25519RecipientStanzaReaderTest.java │ ├── SshEd25519RecipientStanzaWriterFactoryTest.java │ ├── SshRsaOpenSshKeyPairReaderTest.java │ ├── SshRsaPublicKeyMarshallerTest.java │ ├── SshRsaPublicKeyReaderTest.java │ ├── SshRsaRecipientStanzaReaderFactoryTest.java │ ├── SshRsaRecipientStanzaReaderTest.java │ ├── SshRsaRecipientStanzaWriterFactoryTest.java │ ├── SshRsaRecipientStanzaWriterTest.java │ ├── StandardEd25519KeyConverterTest.java │ ├── StandardPublicKeyFingerprintProducerTest.java │ ├── X25519BasePointPublicKeyTest.java │ ├── X25519KeyAgreementFactoryTest.java │ ├── X25519KeyPairGeneratorFactoryTest.java │ └── X25519SharedSecretKeyProducerTest.java ├── jagged-test ├── pom.xml └── src │ └── test │ ├── java │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ └── test │ │ ├── CommunityCryptographyExpectation.java │ │ ├── CommunityCryptographyProperty.java │ │ ├── CommunityCryptographyTest.java │ │ └── FrameworkTest.java │ └── resources │ └── java.security ├── jagged-x25519 ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── exceptionfactory │ │ └── jagged │ │ └── x25519 │ │ ├── BasePointPublicKey.java │ │ ├── IdentityIndicator.java │ │ ├── KeyAgreementFactory.java │ │ ├── KeyPairGeneratorFactory.java │ │ ├── RecipientIndicator.java │ │ ├── RecipientKeyFactory.java │ │ ├── RecipientKeyType.java │ │ ├── SharedSecretKeyProducer.java │ │ ├── SharedWrapKeyProducer.java │ │ ├── StandardRecipientKeyFactory.java │ │ ├── X25519KeyFactory.java │ │ ├── X25519KeyFactorySpi.java │ │ ├── X25519KeyPairGenerator.java │ │ ├── X25519PrivateKey.java │ │ ├── X25519PublicKey.java │ │ ├── X25519RecipientStanza.java │ │ ├── X25519RecipientStanzaReader.java │ │ ├── X25519RecipientStanzaReaderFactory.java │ │ ├── X25519RecipientStanzaWriter.java │ │ ├── X25519RecipientStanzaWriterFactory.java │ │ ├── X25519SharedSecretKeyProducer.java │ │ └── X25519SharedWrapKeyProducer.java │ └── test │ └── java │ └── com │ └── exceptionfactory │ └── jagged │ └── x25519 │ ├── BasePointPublicKeyTest.java │ ├── KeyAgreementFactoryTest.java │ ├── KeyPairGeneratorFactoryTest.java │ ├── StandardRecipientKeyFactoryTest.java │ ├── X25519KeyFactoryTest.java │ ├── X25519KeyPairGeneratorTest.java │ ├── X25519RecipientStanzaReaderFactoryTest.java │ ├── X25519RecipientStanzaReaderTest.java │ ├── X25519RecipientStanzaTest.java │ ├── X25519RecipientStanzaWriterFactoryTest.java │ ├── X25519RecipientStanzaWriterTest.java │ ├── X25519SharedSecretKeyProducerTest.java │ └── X25519SharedWrapKeyProducerTest.java ├── mvnw ├── mvnw.cmd └── pom.xml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | permissions: 12 | security-events: write 13 | contents: read 14 | 15 | jobs: 16 | build-java-21: 17 | runs-on: ubuntu-latest 18 | name: Java 21 19 | steps: 20 | - name: Checkout Sources 21 | uses: actions/checkout@v4 22 | - name: Initialize CodeQL 23 | uses: github/codeql-action/init@v3 24 | with: 25 | languages: java 26 | - name: Setup Java JDK 27 | uses: actions/setup-java@v4 28 | with: 29 | java-version: '21' 30 | distribution: 'zulu' 31 | cache: 'maven' 32 | - name: Build 33 | run: ./mvnw --batch-mode --update-snapshots verify 34 | - name: Perform CodeQL Analysis 35 | uses: github/codeql-action/analyze@v3 36 | - name: Codecov 37 | uses: codecov/codecov-action@v4 38 | with: 39 | token: ${{ secrets.CODECOV_TOKEN }} 40 | build-java-17: 41 | runs-on: ubuntu-latest 42 | name: Java 17 43 | steps: 44 | - name: Checkout Sources 45 | uses: actions/checkout@v4 46 | - name: Setup Java JDK 47 | uses: actions/setup-java@v4 48 | with: 49 | java-version: '17' 50 | distribution: 'zulu' 51 | cache: 'maven' 52 | - name: Build 53 | run: ./mvnw --batch-mode --update-snapshots verify 54 | build-java-11: 55 | runs-on: ubuntu-latest 56 | name: Java 11 57 | steps: 58 | - name: Checkout Sources 59 | uses: actions/checkout@v4 60 | - name: Setup Java JDK 61 | uses: actions/setup-java@v4 62 | with: 63 | java-version: '11' 64 | distribution: 'zulu' 65 | cache: 'maven' 66 | - name: Build 67 | run: ./mvnw --batch-mode --update-snapshots verify 68 | build-java-8: 69 | runs-on: ubuntu-latest 70 | name: Java 8 71 | steps: 72 | - name: Checkout Sources 73 | uses: actions/checkout@v4 74 | - name: Setup Java JDK 75 | uses: actions/setup-java@v4 76 | with: 77 | java-version: '8' 78 | distribution: 'zulu' 79 | cache: 'maven' 80 | - name: Build 81 | run: ./mvnw --batch-mode --update-snapshots verify 82 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout Sources 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - name: Setup Java JDK 18 | uses: actions/setup-java@v4 19 | with: 20 | java-version: '17' 21 | distribution: 'zulu' 22 | cache: 'maven' 23 | - name: Stage Artifacts 24 | run: ./mvnw --batch-mode deploy 25 | - name: Release 26 | run: ./mvnw --batch-mode jreleaser:sign jreleaser:deploy 27 | env: 28 | JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 30 | JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }} 31 | JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_SECRET_KEY }} 32 | JRELEASER_NEXUS2_USERNAME: ${{ secrets.NEXUS2_USERNAME }} 33 | JRELEASER_NEXUS2_PASSWORD: ${{ secrets.NEXUS2_PASSWORD }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | target/ 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.nar 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | 26 | # IntelliJ 27 | .idea/ 28 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar 19 | -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting Vulnerabilities 4 | 5 | The project welcomes responsible reporting of security issues. 6 | 7 | Please allow a reasonable amount of time for evaluation of reported vulnerabilities. 8 | 9 | Vulnerability reports should include the following information: 10 | 11 | - Summary of the problem 12 | - Details of the problem including platform and version information 13 | - Proof of concept to reproduce the problem 14 | - Impact of the problem 15 | 16 | ## Reporting Methods 17 | 18 | Please report vulnerabilities using 19 | [GitHub Security Advisories](https://github.com/exceptionfactory/jagged/security/advisories/new) for initial evaluation 20 | from project maintainers. 21 | -------------------------------------------------------------------------------- /jagged-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.exceptionfactory.jagged 9 | jagged 10 | 1.0.1-SNAPSHOT 11 | 12 | 13 | jagged-api 14 | jagged-api 15 | Jagged public interface components for age encryption 16 | https://github.com/exceptionfactory/jagged 17 | 18 | 19 | 20 | org.junit.jupiter 21 | junit-jupiter-api 22 | test 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-jar-plugin 31 | 32 | 33 | 34 | ${project.groupId} 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/DecryptingChannelFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.io.IOException; 19 | import java.nio.channels.ReadableByteChannel; 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * Abstraction for creating readable Channels supporting streaming age decryption 24 | */ 25 | public interface DecryptingChannelFactory { 26 | /** 27 | * Create new channel that reads and decrypts from the supplied input channel 28 | * 29 | * @param inputChannel Input Channel source containing encrypted bytes 30 | * @param recipientStanzaReaders Recipient Stanza Readers capable of providing the Identity to read the File Key for decryption 31 | * @return Readable Byte Channel containing decrypted bytes 32 | * @throws GeneralSecurityException Thrown on failures while processing recipients or performing cipher operations 33 | * @throws IOException Thrown on failures to read Channel or Recipient Stanzas 34 | */ 35 | ReadableByteChannel newDecryptingChannel( 36 | ReadableByteChannel inputChannel, 37 | Iterable recipientStanzaReaders 38 | ) throws GeneralSecurityException, IOException; 39 | } 40 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/EncryptingChannelFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.io.IOException; 19 | import java.nio.channels.WritableByteChannel; 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * Abstraction for creating writable Channels supporting streaming age encryption 24 | */ 25 | public interface EncryptingChannelFactory { 26 | /** 27 | * Create new channel that encrypts and writes to the supplied output channel 28 | * 29 | * @param outputChannel Output Channel destination for encrypted bytes 30 | * @param recipientStanzaWriters One or more Recipient Stanza Writers for intended recipients 31 | * @return Writable Byte Channel for encrypted bytes 32 | * @throws GeneralSecurityException Thrown on failures writing recipients or performing cipher operations 33 | * @throws IOException Thrown on failures to write Channel or Recipient Stanzas 34 | */ 35 | WritableByteChannel newEncryptingChannel( 36 | WritableByteChannel outputChannel, 37 | Iterable recipientStanzaWriters 38 | ) throws GeneralSecurityException, IOException; 39 | } 40 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/PayloadException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.io.IOException; 19 | 20 | /** 21 | * Payload Exception indicating problems reading or writing File Payload 22 | */ 23 | public class PayloadException extends IOException { 24 | /** 25 | * Payload Exception with message 26 | * 27 | * @param message Message describing the problem with the Payload 28 | */ 29 | public PayloadException(final String message) { 30 | super(message); 31 | } 32 | 33 | /** 34 | * Payload Exception with message and cause 35 | * 36 | * @param message Message describing the problem with the Payload 37 | * @param cause Throwable cause for the Payload Exception 38 | */ 39 | public PayloadException(final String message, final Throwable cause) { 40 | super(message, cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/RecipientStanza.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * Recipient Stanza describes a section of the age header encapsulating the information required to derive a File Key 22 | */ 23 | public interface RecipientStanza { 24 | /** 25 | * Get Recipient Stanza Type returns the first argument from the header section 26 | * 27 | * @return Recipient Stanza Type 28 | */ 29 | String getType(); 30 | 31 | /** 32 | * Get zero or more Recipient Stanza arguments located after the Stanza Type in the header section 33 | * 34 | * @return Recipient Stanza Arguments 35 | */ 36 | List getArguments(); 37 | 38 | /** 39 | * Get Recipient Stanza Body decoded from Base64 representation in header section 40 | * 41 | * @return Recipient Stanza Body 42 | */ 43 | byte[] getBody(); 44 | } 45 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/RecipientStanzaReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.security.GeneralSecurityException; 19 | 20 | /** 21 | * Identity abstraction for reading Recipient Stanzas and returning a File Key 22 | */ 23 | public interface RecipientStanzaReader { 24 | /** 25 | * Get File Key reads one or more Recipient Stanzas and return a File Key of 16 bytes 26 | * 27 | * @param recipientStanzas One or more Recipient Stanzas parsed from the age file header 28 | * @return File Key decrypted from matching Recipient Stanza in age file header 29 | * @throws GeneralSecurityException Thrown on failure to decrypt File Key or process Recipient Stanzas 30 | */ 31 | FileKey getFileKey(Iterable recipientStanzas) throws GeneralSecurityException; 32 | } 33 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/RecipientStanzaWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.security.GeneralSecurityException; 19 | 20 | /** 21 | * Recipient abstraction for reading a File Key and writing a Recipient Stanza with wrapped key arguments 22 | */ 23 | public interface RecipientStanzaWriter { 24 | /** 25 | * Get Recipient Stanzas wrapping a File Key of 16 bytes 26 | * 27 | * @param fileKey File Key 28 | * @return Recipient Stanzas with wrapped key arguments 29 | * @throws GeneralSecurityException Thrown failures to write a Recipient Stanzas from the File Key 30 | */ 31 | Iterable getRecipientStanzas(FileKey fileKey) throws GeneralSecurityException; 32 | } 33 | -------------------------------------------------------------------------------- /jagged-api/src/main/java/com/exceptionfactory/jagged/UnsupportedRecipientStanzaException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged; 17 | 18 | import java.security.GeneralSecurityException; 19 | 20 | /** 21 | * Unsupported Recipient Stanza Exception indicating incorrect formatting or no matched Recipient Stanza found in age file header 22 | */ 23 | public class UnsupportedRecipientStanzaException extends GeneralSecurityException { 24 | /** 25 | * Unsupported Recipient Stanza Exception constructor with message providing additional details 26 | * 27 | * @param message Message providing additional details 28 | */ 29 | public UnsupportedRecipientStanzaException(final String message) { 30 | super(message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jagged-bech32/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.exceptionfactory.jagged 9 | jagged 10 | 1.0.1-SNAPSHOT 11 | 12 | 13 | jagged-bech32 14 | jagged-bech32 15 | Jagged Bech32 encoding for age encryption 16 | https://github.com/exceptionfactory/jagged 17 | 18 | 19 | 20 | org.junit.jupiter 21 | junit-jupiter-api 22 | test 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-jar-plugin 31 | 32 | 33 | 34 | ${project.groupId}.bech32 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /jagged-bech32/src/main/java/com/exceptionfactory/jagged/bech32/Bech32Address.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.bech32; 17 | 18 | /** 19 | * Bech32 Address containing the human-readable part and the data without the separator or checksum 20 | */ 21 | public interface Bech32Address { 22 | /** 23 | * Get the human-readable part of the address prior to the separator 24 | * 25 | * @return Human-readable part of the address 26 | */ 27 | CharSequence getHumanReadablePart(); 28 | 29 | /** 30 | * Get the data portion of the address after the separator but without the trailing checksum 31 | * 32 | * @return Data portion of the address 33 | */ 34 | byte[] getData(); 35 | } 36 | -------------------------------------------------------------------------------- /jagged-bech32/src/main/java/com/exceptionfactory/jagged/bech32/StandardBech32Address.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.bech32; 17 | 18 | import java.util.Objects; 19 | 20 | /** 21 | * Standard implementation of Bech32 Address 22 | */ 23 | class StandardBech32Address implements Bech32Address { 24 | private final CharSequence humanReadablePart; 25 | 26 | private final byte[] data; 27 | 28 | /** 29 | * Standard constructor with required properties 30 | * 31 | * @param humanReadablePart Human-Readable Part characters 32 | * @param data Data bytes 33 | */ 34 | StandardBech32Address(final CharSequence humanReadablePart, final byte[] data) { 35 | this.humanReadablePart = Objects.requireNonNull(humanReadablePart, "Human-Readable Part required"); 36 | this.data = Objects.requireNonNull(data, "Data required"); 37 | } 38 | 39 | /** 40 | * Get Human-Readable Part characters 41 | * 42 | * @return Human-Readable Part 43 | */ 44 | @Override 45 | public CharSequence getHumanReadablePart() { 46 | return humanReadablePart; 47 | } 48 | 49 | /** 50 | * Get Data decoded bytes without checksum 51 | * 52 | * @return Data bytes 53 | */ 54 | @Override 55 | public byte[] getData() { 56 | return data.clone(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /jagged-bom/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.exceptionfactory.jagged 9 | jagged 10 | 1.0.1-SNAPSHOT 11 | 12 | 13 | jagged-bom 14 | jagged-bom 15 | pom 16 | Jagged Bill of Materials for age encryption 17 | https://github.com/exceptionfactory/jagged 18 | 19 | 20 | 21 | 22 | com.exceptionfactory.jagged 23 | jagged-api 24 | 1.0.1-SNAPSHOT 25 | 26 | 27 | com.exceptionfactory.jagged 28 | jagged-framework 29 | 1.0.1-SNAPSHOT 30 | 31 | 32 | com.exceptionfactory.jagged 33 | jagged-scrypt 34 | 1.0.1-SNAPSHOT 35 | 36 | 37 | com.exceptionfactory.jagged 38 | jagged-ssh 39 | 1.0.1-SNAPSHOT 40 | 41 | 42 | com.exceptionfactory.jagged 43 | jagged-bech32 44 | 1.0.1-SNAPSHOT 45 | 46 | 47 | com.exceptionfactory.jagged 48 | jagged-x25519 49 | 1.0.1-SNAPSHOT 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /jagged-framework/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.exceptionfactory.jagged 9 | jagged 10 | 1.0.1-SNAPSHOT 11 | 12 | 13 | jagged-framework 14 | jagged-framework 15 | Jagged framework components for age encryption 16 | https://github.com/exceptionfactory/jagged 17 | 18 | 19 | 20 | com.exceptionfactory.jagged 21 | jagged-api 22 | 23 | 24 | org.junit.jupiter 25 | junit-jupiter-api 26 | test 27 | 28 | 29 | org.mockito 30 | mockito-core 31 | test 32 | 33 | 34 | org.mockito 35 | mockito-junit-jupiter 36 | test 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-jar-plugin 45 | 46 | 47 | 48 | ${project.groupId}.framework 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/armor/ArmoredDecodingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.armor; 17 | 18 | import java.io.IOException; 19 | 20 | /** 21 | * Armored Decoding Exception indicates failures while reading age encryption armored messages 22 | */ 23 | public class ArmoredDecodingException extends IOException { 24 | /** 25 | * Armored Decoding Exception with required message indicating problem details 26 | * 27 | * @param message Exception message with problem details 28 | */ 29 | public ArmoredDecodingException(final String message) { 30 | super(message); 31 | } 32 | 33 | /** 34 | * Armored Decoding Exception with required message indicating problem details 35 | * 36 | * @param message Exception message with problem details 37 | * @param cause Throwable cause of armored decoding failures 38 | */ 39 | public ArmoredDecodingException(final String message, final Throwable cause) { 40 | super(message, cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/armor/ArmoredIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.armor; 17 | 18 | import java.nio.charset.StandardCharsets; 19 | 20 | /** 21 | * age-encryption Armored Indicator for header and footer information 22 | */ 23 | enum ArmoredIndicator { 24 | /** PEM Header */ 25 | HEADER("-----BEGIN AGE ENCRYPTED FILE-----"), 26 | 27 | /** PEM Footer */ 28 | FOOTER("-----END AGE ENCRYPTED FILE-----"); 29 | 30 | private final byte[] indicator; 31 | 32 | ArmoredIndicator(final String indicator) { 33 | this.indicator = indicator.getBytes(StandardCharsets.UTF_8); 34 | } 35 | 36 | byte[] getIndicator() { 37 | return indicator; 38 | } 39 | 40 | int getLength() { 41 | return indicator.length; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/armor/ArmoredSeparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.armor; 17 | 18 | /** 19 | * age-encryption Armored Separator for encoded elements 20 | */ 21 | enum ArmoredSeparator { 22 | /** Line Feed Character */ 23 | LINE_FEED(10), 24 | 25 | /** Carriage Return Character */ 26 | CARRIAGE_RETURN(13); 27 | 28 | private final byte code; 29 | 30 | ArmoredSeparator(final int code) { 31 | this.code = (byte) code; 32 | } 33 | 34 | byte getCode() { 35 | return code; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/codec/CanonicalBase64Encoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.codec; 17 | 18 | import java.util.Base64; 19 | import java.util.Objects; 20 | 21 | /** 22 | * Canonical implementation of Base64 Encoder does not include padding 23 | */ 24 | class CanonicalBase64Encoder implements CanonicalBase64.Encoder { 25 | private static final Base64.Encoder ENCODER_WITHOUT_PADDING = Base64.getEncoder().withoutPadding(); 26 | 27 | /** 28 | * Encode byte array as Base64 characters from standard byte array 29 | * 30 | * @param source Sequence of bytes to be encoded 31 | * @return Base64 encoded byte array 32 | * @throws IllegalArgumentException Thrown when encountering invalid sources 33 | */ 34 | @Override 35 | public byte[] encode(final byte[] source) { 36 | Objects.requireNonNull(source, "Source required for encoding"); 37 | return ENCODER_WITHOUT_PADDING.encode(source); 38 | } 39 | 40 | /** 41 | * Encode byte array to Base64 character string from standard byte array 42 | * 43 | * @param source Sequence of bytes to be encoded 44 | * @return Base64 encoded string 45 | * @throws IllegalArgumentException Thrown when encountering invalid sources 46 | */ 47 | @Override 48 | public String encodeToString(final byte[] source) { 49 | Objects.requireNonNull(source, "Source required for encoding"); 50 | return ENCODER_WITHOUT_PADDING.encodeToString(source); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/ByteBufferCipherFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import javax.crypto.spec.IvParameterSpec; 19 | import java.security.GeneralSecurityException; 20 | 21 | /** 22 | * Byte Buffer Cipher Factory provides instances of Byte Buffer Decryptor and Encryptor objects 23 | */ 24 | public interface ByteBufferCipherFactory { 25 | /** 26 | * Create new instance of Byte Buffer Decryptor using provided Key and Initialization Vector 27 | * 28 | * @param cipherKey Cipher Key required 29 | * @param parameterSpec Initialization Vector parameter specification required 30 | * @return Byte Buffer Decryptor 31 | * @throws GeneralSecurityException Thrown on decryptor initialization failures 32 | */ 33 | ByteBufferDecryptor newByteBufferDecryptor(CipherKey cipherKey, IvParameterSpec parameterSpec) throws GeneralSecurityException; 34 | 35 | /** 36 | * Create new instance of Byte Buffer Encryptor using provided Key and Initialization Vector 37 | * 38 | * @param cipherKey Cipher Key required 39 | * @param parameterSpec Initialization Vector parameter specification required 40 | * @return Byte Buffer Encryptor 41 | * @throws GeneralSecurityException Thrown on encryptor initialization failures 42 | */ 43 | ByteBufferEncryptor newByteBufferEncryptor(CipherKey cipherKey, IvParameterSpec parameterSpec) throws GeneralSecurityException; 44 | } 45 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/ByteBufferDecryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | 21 | /** 22 | * Byte Buffer Decryptor abstracts Cipher decryption operations 23 | */ 24 | public interface ByteBufferDecryptor { 25 | /** 26 | * Read encrypted input buffer and write decrypted bytes to output buffer 27 | * 28 | * @param inputBuffer Encrypted Input Byte Buffer 29 | * @param outputBuffer Decrypted Output Byte Buffer 30 | * @return Number of bytes stored in Output Byte Buffer 31 | * @throws GeneralSecurityException Thrown on decryption failures 32 | */ 33 | int decrypt(ByteBuffer inputBuffer, ByteBuffer outputBuffer) throws GeneralSecurityException; 34 | } 35 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/ByteBufferEncryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | 21 | /** 22 | * Byte Buffer Encryptor abstracts Cipher encryption operations 23 | */ 24 | public interface ByteBufferEncryptor { 25 | /** 26 | * Read input buffer and write encrypted bytes to output buffer 27 | * 28 | * @param inputBuffer Input Byte Buffer 29 | * @param outputBuffer Encrypted Output Byte Buffer 30 | * @return Number of bytes stored in Output Byte Buffer 31 | * @throws GeneralSecurityException Thrown on encryption failures 32 | */ 33 | int encrypt(ByteBuffer inputBuffer, ByteBuffer outputBuffer) throws GeneralSecurityException; 34 | } 35 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/CipherKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Cipher Key extension of Cryptographic Algorithm Key with ChaCha20-Poly1305 20 | */ 21 | public final class CipherKey extends CryptographicAlgorithmKey { 22 | /** 23 | * Cipher Key constructor with required symmetric key 24 | * 25 | * @param key Symmetric Key consisting of 32 bytes 26 | */ 27 | public CipherKey(final byte[] key) { 28 | super(key, CryptographicKeyType.CIPHER_KEY, CryptographicAlgorithm.CHACHA20_POLY1305); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/CryptographicAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Cryptographic Algorithm references 20 | */ 21 | enum CryptographicAlgorithm { 22 | /** ChaCha20-Poly1305 Authenticated Encryption with Associated Data algorithm defined in RFC 7539 */ 23 | CHACHA20_POLY1305("ChaCha20-Poly1305"), 24 | 25 | /** Keyed-Hash Message Authentication Code with SHA-256 defined in RFC 2104 */ 26 | HMACSHA256("HmacSHA256"); 27 | 28 | private final String algorithm; 29 | 30 | CryptographicAlgorithm(final String algorithm) { 31 | this.algorithm = algorithm; 32 | } 33 | 34 | /** 35 | * Get algorithm name as defined according to Java Security Standard Names 36 | * 37 | * @return Java Security Standard Name for Cipher Algorithm 38 | */ 39 | String getAlgorithm() { 40 | return algorithm; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/CryptographicKeyDescription.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Abstraction for describing Cryptographic Key properties 20 | */ 21 | public interface CryptographicKeyDescription { 22 | /** 23 | * Get key length in bytes 24 | * 25 | * @return Key length in bytes 26 | */ 27 | int getKeyLength(); 28 | } 29 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/CryptographicKeyType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Cryptographic Key Type references for construction and validation 20 | */ 21 | enum CryptographicKeyType implements CryptographicKeyDescription { 22 | /** Extracted intermediate key for subsequent expansion */ 23 | EXTRACTED_KEY(32), 24 | 25 | /** Encrypted File Key */ 26 | ENCRYPTED_FILE_KEY(32), 27 | 28 | /** Header Key */ 29 | HEADER_KEY(32), 30 | 31 | /** Cipher Key */ 32 | CIPHER_KEY(32), 33 | 34 | /** Payload Nonce */ 35 | PAYLOAD_NONCE(16), 36 | 37 | /** Shared Salt Key */ 38 | SHARED_SALT(64), 39 | 40 | /** Shared Secret Key */ 41 | SHARED_SECRET(32); 42 | 43 | private final int keyLength; 44 | 45 | CryptographicKeyType(final int keyLength) { 46 | this.keyLength = keyLength; 47 | } 48 | 49 | /** 50 | * Get key length in bytes 51 | * 52 | * @return Key length in bytes 53 | */ 54 | @Override 55 | public int getKeyLength() { 56 | return keyLength; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/EncryptedFileKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * File Key extension of Cryptographic Algorithm Key encrypted with ChaCha20-Poly1305 20 | */ 21 | public final class EncryptedFileKey extends CryptographicAlgorithmKey { 22 | /** 23 | * Encrypted File Key constructor with required key byte array 24 | * 25 | * @param key Encrypted Key consisting of 32 bytes 26 | */ 27 | public EncryptedFileKey(final byte[] key) { 28 | super(key, CryptographicKeyType.ENCRYPTED_FILE_KEY, CryptographicAlgorithm.CHACHA20_POLY1305); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/FileKeyDecryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * File Key Decryptor abstracts cipher operations for decrypting a File Key 24 | */ 25 | public interface FileKeyDecryptor { 26 | /** 27 | * Get File Key from Encrypted File Key 28 | * 29 | * @param encryptedFileKey Encrypted File Key 30 | * @param cipherKey Cipher Key for decrypting File Key 31 | * @return Decrypted File Key 32 | * @throws GeneralSecurityException Thrown on failure of decryption operations 33 | */ 34 | FileKey getFileKey(EncryptedFileKey encryptedFileKey, CipherKey cipherKey) throws GeneralSecurityException; 35 | } 36 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/FileKeyDecryptorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.security.Provider; 19 | import java.util.Objects; 20 | 21 | /** 22 | * Factory abstraction for instances of File Key Decryptor 23 | */ 24 | public final class FileKeyDecryptorFactory { 25 | private final Provider provider; 26 | 27 | /** 28 | * File Key Decryptor Factory uses the system default Security Provider configuration 29 | */ 30 | public FileKeyDecryptorFactory() { 31 | provider = null; 32 | } 33 | 34 | /** 35 | * File Key Decryptor Factory with support for custom Security Provider 36 | * 37 | * @param provider Security Provider supporting ChaCha20-Poly1305 38 | */ 39 | public FileKeyDecryptorFactory(final Provider provider) { 40 | this.provider = Objects.requireNonNull(provider, "Provider required"); 41 | } 42 | 43 | /** 44 | * Create new instance of File Key Decryptor using current configuration 45 | * 46 | * @return File Key Decryptor 47 | */ 48 | public FileKeyDecryptor newFileKeyDecryptor() { 49 | return provider == null ? new StandardFileKeyDecryptor() : new StandardFileKeyDecryptor(provider); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/FileKeyEncryptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * File Key Encryptor abstracts cipher operations for encrypting a File Key 24 | */ 25 | public interface FileKeyEncryptor { 26 | /** 27 | * Get Encrypted File Key from File Key 28 | * 29 | * @param fileKey File Key 30 | * @param cipherKey Cipher Key for encrypting File Key 31 | * @return Encrypted File Key 32 | * @throws GeneralSecurityException Thrown on failure of encryption operations 33 | */ 34 | EncryptedFileKey getEncryptedFileKey(FileKey fileKey, CipherKey cipherKey) throws GeneralSecurityException; 35 | } 36 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/FileKeyEncryptorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.security.Provider; 19 | import java.util.Objects; 20 | 21 | /** 22 | * Factory abstraction for instances of File Key Encryptor 23 | */ 24 | public final class FileKeyEncryptorFactory { 25 | private final Provider provider; 26 | 27 | /** 28 | * File Key Encryptor Factory uses the system default Security Provider configuration 29 | */ 30 | public FileKeyEncryptorFactory() { 31 | provider = null; 32 | } 33 | 34 | /** 35 | * File Key Encryptor Factory with support for custom Security Provider 36 | * 37 | * @param provider Security Provider supporting ChaCha20-Poly1305 38 | */ 39 | public FileKeyEncryptorFactory(final Provider provider) { 40 | this.provider = Objects.requireNonNull(provider, "Provider required"); 41 | } 42 | 43 | /** 44 | * Create new instance of File Key Encryptor using current configuration 45 | * 46 | * @return File Key Encryptor 47 | */ 48 | public FileKeyEncryptor newFileKeyEncryptor() { 49 | return provider == null ? new StandardFileKeyEncryptor() : new StandardFileKeyEncryptor(provider); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/FileKeyIvParameterSpec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import javax.crypto.spec.IvParameterSpec; 19 | 20 | /** 21 | * Initialization Vector Parameter Specification for File Key encryption and decryption consisting of 12 zero bytes 22 | */ 23 | final class FileKeyIvParameterSpec extends IvParameterSpec { 24 | private static final int INITIALIZATION_VECTOR_LENGTH = 12; 25 | 26 | /** 27 | * File Key Initialization Vector Parameter Specification with array of 12 bytes 28 | */ 29 | FileKeyIvParameterSpec() { 30 | super(new byte[INITIALIZATION_VECTOR_LENGTH]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/HeaderKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * Abstraction for producing Header MAC Key using HMAC-based Extract-and-Expand Key Derivation Function described in RFC 5869 24 | */ 25 | public interface HeaderKeyProducer { 26 | /** 27 | * Get derived Header Message Authentication Code Key 28 | * 29 | * @param fileKey File Key 30 | * @return Message Authentication Code Header Key 31 | * @throws GeneralSecurityException Thrown on key derivation failures 32 | */ 33 | MacKey getHeaderKey(FileKey fileKey) throws GeneralSecurityException; 34 | } 35 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/HeaderKeyProducerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Header Key Producer Factory creates new producers using required arguments 20 | */ 21 | public final class HeaderKeyProducerFactory { 22 | private HeaderKeyProducerFactory() { 23 | 24 | } 25 | 26 | /** 27 | * Create a new instance of Header Key Producer 28 | * 29 | * @return Header Key Producer 30 | */ 31 | public static HeaderKeyProducer newHeaderKeyProducer() { 32 | return new StandardHeaderKeyProducer(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/MacKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Message Authentication Code Key extension of Cryptographic Algorithm Key using HmacSHA256 20 | */ 21 | public class MacKey extends CryptographicAlgorithmKey { 22 | /** 23 | * Message Authentication Code Key constructor with required symmetric key 24 | * 25 | * @param key Symmetric Key with byte length based on Cryptographic Key Type 26 | * @param cryptographicKeyDescription Cryptographic Key Description 27 | */ 28 | public MacKey(final byte[] key, final CryptographicKeyDescription cryptographicKeyDescription) { 29 | super(key, cryptographicKeyDescription, CryptographicAlgorithm.HMACSHA256); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/MessageAuthenticationCodeProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.nio.ByteBuffer; 19 | 20 | /** 21 | * Producer abstraction for generating a Keyed-Hash Message Authentication Code as described in RFC 2104 22 | */ 23 | public interface MessageAuthenticationCodeProducer { 24 | /** 25 | * Get Message Authentication Code using configured Key and provided input bytes 26 | * 27 | * @param inputBuffer Input Buffer from which to produce a Message Authentication Code 28 | * @return Message Authentication Code bytes 29 | */ 30 | byte[] getMessageAuthenticationCode(ByteBuffer inputBuffer); 31 | } 32 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/MessageAuthenticationCodeProducerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.security.GeneralSecurityException; 19 | 20 | /** 21 | * Message Authentication Code Producer Factory creates new producers using required arguments 22 | */ 23 | public final class MessageAuthenticationCodeProducerFactory { 24 | private MessageAuthenticationCodeProducerFactory() { 25 | 26 | } 27 | 28 | /** 29 | * Create a new instance of Message Authentication Code Producer using MAC Key 30 | * 31 | * @param macKey Message Authentication Code Key required 32 | * @return Message Authentication Code Producer 33 | * @throws GeneralSecurityException Thrown on producer initialization failures 34 | */ 35 | public static MessageAuthenticationCodeProducer newMessageAuthenticationCodeProducer(final MacKey macKey) throws GeneralSecurityException { 36 | return new StandardMessageAuthenticationCodeProducer(macKey); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/PayloadKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * Abstraction for producing Payload Key from File Key using HMAC-based Extract-and-Expand Key Derivation Function described in RFC 5869 24 | */ 25 | public interface PayloadKeyProducer { 26 | /** 27 | * Get Payload Key 28 | * 29 | * @param fileKey File Key 30 | * @param payloadNonceKey Payload Nonce Key 31 | * @return Payload Cipher Key 32 | * @throws GeneralSecurityException Thrown on key derivation failures 33 | */ 34 | CipherKey getPayloadKey(FileKey fileKey, PayloadNonceKey payloadNonceKey) throws GeneralSecurityException; 35 | } 36 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/PayloadKeyProducerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Payload Key Producer Factory creates new producers 20 | */ 21 | public final class PayloadKeyProducerFactory { 22 | private PayloadKeyProducerFactory() { 23 | 24 | } 25 | 26 | /** 27 | * Create a new Payload Key Producer 28 | * 29 | * @return Payload Key Producer 30 | */ 31 | public static PayloadKeyProducer newPayloadKeyProducer() { 32 | return new StandardPayloadKeyProducer(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/PayloadNonceKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import java.security.SecureRandom; 19 | 20 | /** 21 | * Payload nonce consisting of 16 bytes from a cryptographically secure pseudorandom number generator 22 | */ 23 | public final class PayloadNonceKey extends MacKey { 24 | private static final SecureRandom SECURE_RANDOM = new SecureRandom(); 25 | 26 | /** 27 | * Payload nonce key constructor generates a new key using java.util.SecureRandom.nextBytes() 28 | */ 29 | public PayloadNonceKey() { 30 | this(getSecureRandomKey()); 31 | } 32 | 33 | /** 34 | * Payload nonce key with required byte array 35 | * 36 | * @param key Nonce consisting of 16 bytes 37 | */ 38 | public PayloadNonceKey(final byte[] key) { 39 | super(key, CryptographicKeyType.PAYLOAD_NONCE); 40 | } 41 | 42 | private static byte[] getSecureRandomKey() { 43 | final byte[] key = new byte[CryptographicKeyType.PAYLOAD_NONCE.getKeyLength()]; 44 | SECURE_RANDOM.nextBytes(key); 45 | return key; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/SharedSaltKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Shared Salt Key extension of Cryptographic Algorithm Key containing the concatenation of shared public keys 20 | */ 21 | public final class SharedSaltKey extends MacKey { 22 | /** 23 | * Shared Salt Key constructor with required symmetric key 24 | * 25 | * @param key Symmetric Key consisting of 64 bytes concatenated from shared public keys 26 | */ 27 | public SharedSaltKey(final byte[] key) { 28 | super(key, CryptographicKeyType.SHARED_SALT); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/SharedSecretKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | /** 19 | * Shared Secret Key extension of Cryptographic Algorithm Key containing the results of key agreement processing 20 | */ 21 | public final class SharedSecretKey extends MacKey { 22 | /** 23 | * Shared Secret Key constructor with required symmetric key 24 | * 25 | * @param key Symmetric Key consisting of 32 bytes 26 | */ 27 | public SharedSecretKey(final byte[] key) { 28 | super(key, CryptographicKeyType.SHARED_SECRET); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/StandardMessageAuthenticationCodeProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import javax.crypto.Mac; 19 | import java.nio.ByteBuffer; 20 | import java.security.GeneralSecurityException; 21 | import java.util.Objects; 22 | 23 | /** 24 | * Standard implementation of Message Authentication Code Producer using javax.crypto.Mac with HMAC-SHA-256 25 | */ 26 | final class StandardMessageAuthenticationCodeProducer implements MessageAuthenticationCodeProducer { 27 | private final Mac mac; 28 | 29 | /** 30 | * Standard Message Authentication Code Producer constructor 31 | * 32 | * @param macKey Message Authentication Code Key required 33 | * @throws GeneralSecurityException Thrown on Message Authentication Code initialization failures 34 | */ 35 | StandardMessageAuthenticationCodeProducer(final MacKey macKey) throws GeneralSecurityException { 36 | mac = Mac.getInstance(macKey.getAlgorithm()); 37 | mac.init(macKey); 38 | } 39 | 40 | /** 41 | * Get Message Authentication Code using configured Key and provided input bytes 42 | * 43 | * @param inputBuffer Input Buffer required 44 | * @return Message Authentication Code bytes derived from HMAC-SHA-256 45 | */ 46 | @Override 47 | public byte[] getMessageAuthenticationCode(final ByteBuffer inputBuffer) { 48 | Objects.requireNonNull(inputBuffer, "Input Buffer required"); 49 | mac.update(inputBuffer); 50 | return mac.doFinal(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/crypto/StandardPayloadKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | import java.util.Objects; 22 | 23 | /** 24 | * Standard implementation of Payload Key Producer using HMAC-based Extract-and-Expand Key Derivation Function described in RFC 5869 25 | */ 26 | class StandardPayloadKeyProducer extends HashedDerivedKeyProducer implements PayloadKeyProducer { 27 | /** Payload Application Information for HKDF-SHA-256 as described in age-encryption Header MAC key derivation */ 28 | private static final byte[] PAYLOAD_INFO = new byte[]{'p', 'a', 'y', 'l', 'o', 'a', 'd'}; 29 | 30 | /** 31 | * Get Payload Key using HKDF-SHA-256 32 | * 33 | * @param fileKey File Key 34 | * @param payloadNonceKey Payload Nonce Key 35 | * @return Payload Cipher Key 36 | * @throws GeneralSecurityException Thrown on key derivation failures 37 | */ 38 | @Override 39 | public CipherKey getPayloadKey(final FileKey fileKey, final PayloadNonceKey payloadNonceKey) throws GeneralSecurityException { 40 | Objects.requireNonNull(fileKey, "File Key required"); 41 | Objects.requireNonNull(payloadNonceKey, "Payload Nonce Key required"); 42 | final byte[] payloadKey = getDerivedKey(fileKey, payloadNonceKey, PAYLOAD_INFO); 43 | return new CipherKey(payloadKey); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/FileHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanza; 19 | 20 | /** 21 | * File Header containing Recipient Stanzas and Message Authentication Code from age-encryption header 22 | */ 23 | interface FileHeader { 24 | /** 25 | * Get Recipient Stanzas read from File Header 26 | * 27 | * @return Recipient Stanzas 28 | */ 29 | Iterable getRecipientStanzas(); 30 | 31 | /** 32 | * Get Message Authentication Code bytes 33 | * 34 | * @return Message Authentication Code bytes 35 | */ 36 | byte[] getMessageAuthenticationCode(); 37 | } 38 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/FileHeaderReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | 21 | /** 22 | * Abstraction for reading age file header containing one or more Recipient Stanzas 23 | */ 24 | interface FileHeaderReader { 25 | /** 26 | * Get File Header with Recipient Stanzas from Channel that starts with standard age header 27 | * 28 | * @param inputBuffer Input Byte Buffer starting with age header 29 | * @return File Header with Recipient Stanzas containing one or more elements 30 | * @throws GeneralSecurityException Thrown on failure to read or process File Header bytes 31 | */ 32 | FileHeader getFileHeader(ByteBuffer inputBuffer) throws GeneralSecurityException; 33 | } 34 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/FileHeaderWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanza; 19 | 20 | import java.io.IOException; 21 | import java.nio.ByteBuffer; 22 | import java.security.GeneralSecurityException; 23 | 24 | /** 25 | * File Header Reader serializes Recipient Stanzas according to standard age encryption specifications 26 | */ 27 | interface FileHeaderWriter { 28 | /** 29 | * Write Recipient Stanzas along with formatted age header elements 30 | * 31 | * @param recipientStanzas Recipient Stanzas 32 | * @return Byte Buffer containing serialized age header 33 | * @throws GeneralSecurityException Thrown on failures to run cryptographic operations while serializing header 34 | * @throws IOException Thrown on failures to write Recipient Stanzas 35 | */ 36 | ByteBuffer writeRecipientStanzas(Iterable recipientStanzas) throws GeneralSecurityException, IOException; 37 | } 38 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/FileKeyReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | import com.exceptionfactory.jagged.RecipientStanzaReader; 20 | 21 | import java.io.IOException; 22 | import java.nio.ByteBuffer; 23 | import java.security.GeneralSecurityException; 24 | 25 | /** 26 | * Abstraction responsible for reading and verifying File Header before returning File Key 27 | */ 28 | interface FileKeyReader { 29 | /** 30 | * Read File Key from File Header buffer using provided Recipient Stanza Reader 31 | * 32 | * @param buffer File Header buffer 33 | * @param recipientStanzaReader Recipient Stanza Reader 34 | * @return File Key 35 | * @throws GeneralSecurityException Thrown on failures to read File Key or verify File Header 36 | * @throws IOException Thrown on failures to read File Header 37 | */ 38 | FileKey readFileKey(ByteBuffer buffer, RecipientStanzaReader recipientStanzaReader) throws GeneralSecurityException, IOException; 39 | } 40 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/HeaderDecodingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import java.security.GeneralSecurityException; 19 | 20 | /** 21 | * Header Decoding Exception indicates failures while reading File Header section of age encryption messages 22 | */ 23 | class HeaderDecodingException extends GeneralSecurityException { 24 | /** 25 | * Header Decoding Exception with required message indicating problem details 26 | * 27 | * @param message Exception message with problem details 28 | */ 29 | HeaderDecodingException(final String message) { 30 | super(message); 31 | } 32 | 33 | /** 34 | * Header Decoding Exception with required message indicating problem details 35 | * 36 | * @param message Exception message with problem details 37 | * @param cause Throwable cause of header failures 38 | */ 39 | HeaderDecodingException(final String message, final Throwable cause) { 40 | super(message, cause); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/PayloadKeyReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanzaReader; 19 | import com.exceptionfactory.jagged.framework.crypto.CipherKey; 20 | 21 | import java.io.IOException; 22 | import java.nio.ByteBuffer; 23 | import java.security.GeneralSecurityException; 24 | 25 | /** 26 | * Abstraction responsible for reading File Header and deriving Payload Key from File Key after header verification 27 | */ 28 | public interface PayloadKeyReader { 29 | /** 30 | * Get Payload Key from File Header buffer using provided Recipient Stanza Readers to read File Key 31 | * 32 | * @param buffer File Header buffer 33 | * @param recipientStanzaReaders Recipient Stanza Readers 34 | * @return Payload Key 35 | * @throws GeneralSecurityException Thrown on failures to derive Payload Key 36 | * @throws IOException Thrown on failures to read File Header 37 | */ 38 | CipherKey getPayloadKey(ByteBuffer buffer, Iterable recipientStanzaReaders) throws GeneralSecurityException, IOException; 39 | } 40 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/PayloadKeyWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanzaWriter; 19 | import com.exceptionfactory.jagged.framework.crypto.CipherKey; 20 | 21 | import java.io.IOException; 22 | import java.nio.ByteBuffer; 23 | import java.security.GeneralSecurityException; 24 | 25 | /** 26 | * Abstraction responsible for writing File Header and returning a Payload Key from a generated File Key 27 | */ 28 | public interface PayloadKeyWriter { 29 | /** 30 | * Write File Header to buffer after generating a File Key and return derived Payload Key 31 | * 32 | * @param buffer Byte Buffer with sufficient capacity for serialized File Header should support at least 128 bytes 33 | * @param recipientStanzaWriters Recipient Stanza Writers 34 | * @return Derived Payload Cipher Key for encryption operations 35 | * @throws GeneralSecurityException Thrown on cipher operation failures 36 | * @throws IOException Thrown on serialization failures 37 | */ 38 | CipherKey writeFileHeader(ByteBuffer buffer, Iterable recipientStanzaWriters) throws GeneralSecurityException, IOException; 39 | } 40 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/SectionIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import java.nio.charset.StandardCharsets; 19 | 20 | /** 21 | * age-encryption Section Indicator for File Header as described in ABNF definitions 22 | */ 23 | enum SectionIndicator { 24 | /** First line with Version 1 */ 25 | VERSION("age-encryption.org/v1"), 26 | 27 | /** Stanza Line indicator */ 28 | STANZA("-> "), 29 | 30 | /** End of header indicator */ 31 | END("---"); 32 | 33 | private final byte[] indicator; 34 | 35 | SectionIndicator(final String indicator) { 36 | this.indicator = indicator.getBytes(StandardCharsets.UTF_8); 37 | } 38 | 39 | byte[] getIndicator() { 40 | return indicator; 41 | } 42 | 43 | int getLength() { 44 | return indicator.length; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/SectionSeparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | /** 19 | * age-encryption Section Separator for File Header elements 20 | */ 21 | enum SectionSeparator { 22 | /** Line Feed Character */ 23 | LINE_FEED(10), 24 | 25 | /** Space Character */ 26 | SPACE(32); 27 | 28 | private final int code; 29 | 30 | SectionSeparator(final int code) { 31 | this.code = code; 32 | } 33 | 34 | int getCode() { 35 | return code; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/StandardFileHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanza; 19 | 20 | import java.util.Objects; 21 | 22 | /** 23 | * Standard implementation of age-encryption File Header 24 | */ 25 | class StandardFileHeader implements FileHeader { 26 | private final Iterable recipientStanzas; 27 | 28 | private final byte[] messageAuthenticationCode; 29 | 30 | StandardFileHeader(final Iterable recipientStanzas, final byte[] messageAuthenticationCode) { 31 | this.recipientStanzas = Objects.requireNonNull(recipientStanzas, "Recipient Stanzas required"); 32 | this.messageAuthenticationCode = Objects.requireNonNull(messageAuthenticationCode, "Message Authentication Code required"); 33 | } 34 | 35 | @Override 36 | public Iterable getRecipientStanzas() { 37 | return recipientStanzas; 38 | } 39 | 40 | @Override 41 | public byte[] getMessageAuthenticationCode() { 42 | return messageAuthenticationCode.clone(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/format/StandardRecipientStanza.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.format; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanza; 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.Objects; 23 | 24 | /** 25 | * Standard implementation of Recipient Stanza 26 | */ 27 | class StandardRecipientStanza implements RecipientStanza { 28 | private final String type; 29 | 30 | private final List arguments; 31 | 32 | private final byte[] body; 33 | 34 | StandardRecipientStanza(final String type, final List arguments, final byte[] body) { 35 | this.type = Objects.requireNonNull(type, "Type required"); 36 | this.arguments = Collections.unmodifiableList(Objects.requireNonNull(arguments, "Arguments required")); 37 | this.body = Objects.requireNonNull(body, "Body required"); 38 | } 39 | 40 | @Override 41 | public String getType() { 42 | return type; 43 | } 44 | 45 | @Override 46 | public List getArguments() { 47 | return arguments; 48 | } 49 | 50 | @Override 51 | public byte[] getBody() { 52 | return body.clone(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jagged-framework/src/main/java/com/exceptionfactory/jagged/framework/stream/ChunkSize.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.stream; 17 | 18 | /** 19 | * STREAM Chunk Size definitions according to age-encryption Payload section 20 | */ 21 | enum ChunkSize { 22 | /** Encrypted chunk size including ChaCha20-Poly1305 tag */ 23 | ENCRYPTED(65552), 24 | 25 | /** Plain chunk size before encryption and after decryption */ 26 | PLAIN(65536); 27 | 28 | private final int size; 29 | 30 | ChunkSize(final int size) { 31 | this.size = size; 32 | } 33 | 34 | /** 35 | * Get chunk size in bytes 36 | * 37 | * @return Chunk size in bytes 38 | */ 39 | public int getSize() { 40 | return size; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/codec/CanonicalBase64DecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.codec; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 23 | import static org.junit.jupiter.api.Assertions.assertThrows; 24 | 25 | class CanonicalBase64DecoderTest { 26 | private static final byte[] DECODED = new byte[]{1}; 27 | 28 | private static final String ENCODED_WITH_PADDING = "AQ=="; 29 | 30 | private static final String ENCODED_WITHOUT_PADDING = "AQ"; 31 | 32 | private static final byte[] ENCODED_BYTES_WITHOUT_PADDING = ENCODED_WITHOUT_PADDING.getBytes(StandardCharsets.ISO_8859_1); 33 | 34 | private static final byte[] ENCODED_BYTE_WITH_PADDING = ENCODED_WITH_PADDING.getBytes(StandardCharsets.ISO_8859_1); 35 | 36 | @Test 37 | void testDecode() { 38 | final CanonicalBase64Decoder decoder = new CanonicalBase64Decoder(); 39 | 40 | final byte[] decoded = decoder.decode(ENCODED_BYTES_WITHOUT_PADDING); 41 | 42 | assertArrayEquals(DECODED, decoded); 43 | } 44 | 45 | @Test 46 | void testDecodeWithPadding() { 47 | final CanonicalBase64Decoder decoder = new CanonicalBase64Decoder(); 48 | 49 | assertThrows(IllegalArgumentException.class, () -> decoder.decode(ENCODED_BYTE_WITH_PADDING)); 50 | } 51 | 52 | @Test 53 | void testDecodeNull() { 54 | final CanonicalBase64Decoder decoder = new CanonicalBase64Decoder(); 55 | 56 | assertThrows(IllegalArgumentException.class, () -> decoder.decode(null)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/codec/CanonicalBase64EncoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.codec; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 23 | import static org.junit.jupiter.api.Assertions.assertEquals; 24 | 25 | class CanonicalBase64EncoderTest { 26 | private static final byte[] SOURCE = new byte[]{1}; 27 | 28 | private static final String ENCODED_WITHOUT_PADDING = "AQ"; 29 | 30 | private static final byte[] ENCODED_BYTES_WITHOUT_PADDING = ENCODED_WITHOUT_PADDING.getBytes(StandardCharsets.ISO_8859_1); 31 | 32 | @Test 33 | void testEncodeToString() { 34 | final CanonicalBase64Encoder encoder = new CanonicalBase64Encoder(); 35 | 36 | final String encoded = encoder.encodeToString(SOURCE); 37 | 38 | assertEquals(ENCODED_WITHOUT_PADDING, encoded); 39 | } 40 | 41 | @Test 42 | void testEncode() { 43 | final CanonicalBase64Encoder encoder = new CanonicalBase64Encoder(); 44 | 45 | final byte[] encoded = encoder.encode(SOURCE); 46 | 47 | assertArrayEquals(ENCODED_BYTES_WITHOUT_PADDING, encoded); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/FileKeyIvParameterSpecTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 21 | 22 | class FileKeyIvParameterSpecTest { 23 | private static final int INITIALIZATION_VECTOR_LENGTH = 12; 24 | 25 | private static final byte[] EMPTY_INITIALIZATION_VECTOR = new byte[INITIALIZATION_VECTOR_LENGTH]; 26 | 27 | @Test 28 | void testParameterSpec() { 29 | final FileKeyIvParameterSpec parameterSpec = new FileKeyIvParameterSpec(); 30 | 31 | final byte[] initializationVector = parameterSpec.getIV(); 32 | 33 | assertArrayEquals(EMPTY_INITIALIZATION_VECTOR, initializationVector); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/HeaderKeyProducerFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertNotNull; 21 | 22 | class HeaderKeyProducerFactoryTest { 23 | 24 | @Test 25 | void testNewHeaderKeyProducer() { 26 | final HeaderKeyProducer headerKeyProducer = HeaderKeyProducerFactory.newHeaderKeyProducer(); 27 | 28 | assertNotNull(headerKeyProducer); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/MacKeyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 21 | import static org.junit.jupiter.api.Assertions.assertEquals; 22 | import static org.junit.jupiter.api.Assertions.assertThrows; 23 | 24 | class MacKeyTest { 25 | static final byte[] SYMMETRIC_KEY = new byte[]{ 26 | 1, 2, 3, 4, 5, 6, 7, 8, 27 | 1, 2, 3, 4, 5, 6, 7, 8, 28 | 1, 2, 3, 4, 5, 6, 7, 8, 29 | 1, 2, 3, 4, 5, 6, 7, 8 30 | }; 31 | 32 | private static final byte[] INVALID_KEY = new byte[0]; 33 | 34 | @Test 35 | void testMacKey() { 36 | final MacKey macKey = new MacKey(SYMMETRIC_KEY, CryptographicKeyType.EXTRACTED_KEY); 37 | 38 | assertEquals(CryptographicAlgorithm.HMACSHA256.getAlgorithm(), macKey.getAlgorithm()); 39 | assertArrayEquals(SYMMETRIC_KEY, macKey.getEncoded()); 40 | } 41 | 42 | @Test 43 | void testMacKeyLengthNotValid() { 44 | assertThrows(IllegalArgumentException.class, () -> new MacKey(INVALID_KEY, CryptographicKeyType.HEADER_KEY)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/MessageAuthenticationCodeProducerFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.security.GeneralSecurityException; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertNotNull; 23 | 24 | class MessageAuthenticationCodeProducerFactoryTest { 25 | 26 | @Test 27 | void testNewByteBufferDecryptor() throws GeneralSecurityException { 28 | final MacKey macKey = new MacKey(MacKeyTest.SYMMETRIC_KEY, CryptographicKeyType.EXTRACTED_KEY); 29 | 30 | final MessageAuthenticationCodeProducer messageAuthenticationCodeProducer = MessageAuthenticationCodeProducerFactory.newMessageAuthenticationCodeProducer(macKey); 31 | 32 | assertNotNull(messageAuthenticationCodeProducer); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/PayloadKeyProducerFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertNotNull; 21 | 22 | class PayloadKeyProducerFactoryTest { 23 | 24 | @Test 25 | void testNewPayloadKeyProducer() { 26 | final PayloadKeyProducer payloadKeyProducer = PayloadKeyProducerFactory.newPayloadKeyProducer(); 27 | 28 | assertNotNull(payloadKeyProducer); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/SharedSaltKeyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 21 | import static org.junit.jupiter.api.Assertions.assertEquals; 22 | import static org.junit.jupiter.api.Assertions.assertThrows; 23 | 24 | class SharedSaltKeyTest { 25 | private static final byte[] INVALID_KEY = new byte[0]; 26 | 27 | @Test 28 | void testSharedSaltKey() { 29 | final byte[] encoded = new byte[CryptographicKeyType.SHARED_SALT.getKeyLength()]; 30 | final SharedSaltKey sharedSaltKey = new SharedSaltKey(encoded); 31 | 32 | assertEquals(CryptographicAlgorithm.HMACSHA256.getAlgorithm(), sharedSaltKey.getAlgorithm()); 33 | assertArrayEquals(encoded, sharedSaltKey.getEncoded()); 34 | } 35 | 36 | @Test 37 | void testSharedSaltKeyLengthNotValid() { 38 | assertThrows(IllegalArgumentException.class, () -> new SharedSaltKey(INVALID_KEY)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/SharedSecretKeyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 21 | import static org.junit.jupiter.api.Assertions.assertEquals; 22 | import static org.junit.jupiter.api.Assertions.assertThrows; 23 | 24 | class SharedSecretKeyTest { 25 | private static final byte[] INVALID_KEY = new byte[0]; 26 | 27 | @Test 28 | void testSharedSecretKey() { 29 | final byte[] encoded = new byte[CryptographicKeyType.SHARED_SECRET.getKeyLength()]; 30 | final SharedSecretKey sharedSecretKey = new SharedSecretKey(encoded); 31 | 32 | assertEquals(CryptographicAlgorithm.HMACSHA256.getAlgorithm(), sharedSecretKey.getAlgorithm()); 33 | assertArrayEquals(encoded, sharedSecretKey.getEncoded()); 34 | } 35 | 36 | @Test 37 | void testSharedSecretKeyLengthNotValid() { 38 | assertThrows(IllegalArgumentException.class, () -> new SharedSecretKey(INVALID_KEY)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/StandardByteBufferEncryptorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import javax.crypto.spec.IvParameterSpec; 21 | import java.nio.ByteBuffer; 22 | import java.security.GeneralSecurityException; 23 | 24 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 25 | import static org.junit.jupiter.api.Assertions.assertEquals; 26 | 27 | class StandardByteBufferEncryptorTest { 28 | static final byte[] INPUT = new byte[]{0, 1, 2, 3}; 29 | 30 | static final byte[] EXPECTED_OUTPUT = new byte[]{75, -23, -98, 103, 100, 11, 71, 7, 38, 115, -38, -46, 11, -73, 78, -45, 13, -4, -82, -6}; 31 | 32 | @Test 33 | void testEncrypt() throws GeneralSecurityException { 34 | final CipherKey cipherKey = new CipherKey(CipherKeyTest.SYMMETRIC_KEY); 35 | final IvParameterSpec parameterSpec = new IvParameterSpec(CipherFactoryTest.INITIALIZATION_VECTOR); 36 | 37 | final CipherFactory cipherFactory = new CipherFactory(); 38 | final StandardByteBufferEncryptor encryptor = new StandardByteBufferEncryptor(cipherFactory, cipherKey, parameterSpec); 39 | 40 | final ByteBuffer inputBuffer = ByteBuffer.wrap(INPUT); 41 | final ByteBuffer outputBuffer = ByteBuffer.allocate(EXPECTED_OUTPUT.length); 42 | 43 | final int encryptedLength = encryptor.encrypt(inputBuffer, outputBuffer); 44 | 45 | assertEquals(EXPECTED_OUTPUT.length, encryptedLength); 46 | final byte[] output = outputBuffer.array(); 47 | assertArrayEquals(EXPECTED_OUTPUT, output); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/StandardHeaderKeyProducerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import com.exceptionfactory.jagged.FileKey; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.security.GeneralSecurityException; 22 | 23 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 24 | import static org.junit.jupiter.api.Assertions.assertNotNull; 25 | 26 | class StandardHeaderKeyProducerTest { 27 | static final byte[] FILE_KEY = new byte[]{ 28 | 1, 2, 3, 4, 5, 6, 7, 8, 29 | 1, 2, 3, 4, 5, 6, 7, 8 30 | }; 31 | 32 | static final byte[] HEADER_KEY = new byte[]{ 33 | 50, 103, -85, -117, 80, 49, -69, -100, 34 | 36, -42, 85, 10, -66, -104, 100, 80, 35 | 90, 31, -92, -51, 54, -3, -4, -83, 36 | 8, -114, -5, 52, 124, -41, 52, 28 37 | }; 38 | 39 | @Test 40 | void testGetHeaderKey() throws GeneralSecurityException { 41 | final FileKey fileKey = new FileKey(FILE_KEY); 42 | 43 | final StandardHeaderKeyProducer producer = new StandardHeaderKeyProducer(); 44 | 45 | final MacKey headerKey = producer.getHeaderKey(fileKey); 46 | 47 | assertNotNull(headerKey); 48 | assertArrayEquals(HEADER_KEY, headerKey.getEncoded()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /jagged-framework/src/test/java/com/exceptionfactory/jagged/framework/crypto/StandardMessageAuthenticationCodeProducerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.framework.crypto; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.nio.ByteBuffer; 21 | import java.security.GeneralSecurityException; 22 | 23 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 24 | 25 | class StandardMessageAuthenticationCodeProducerTest { 26 | static final byte[] INPUT = new byte[]{0, 1, 2, 3}; 27 | 28 | static final byte[] EXPECTED_OUTPUT = new byte[]{ 29 | -70, 108, -59, 94, 32, 29, 24, 57, 30 | 18, -109, -115, 55, -64, -37, 70, -112, 31 | -16, 5, -115, -73, 109, 76, -4, 51, 32 | -39, -103, 92, 17, -30, -26, -10, -70 33 | }; 34 | 35 | @Test 36 | void testGetMessageAuthenticationCode() throws GeneralSecurityException { 37 | final MacKey macKey = new MacKey(MacKeyTest.SYMMETRIC_KEY, CryptographicKeyType.EXTRACTED_KEY); 38 | final StandardMessageAuthenticationCodeProducer producer = new StandardMessageAuthenticationCodeProducer(macKey); 39 | 40 | final ByteBuffer inputBuffer = ByteBuffer.wrap(INPUT); 41 | final byte[] messageAuthenticationCode = producer.getMessageAuthenticationCode(inputBuffer); 42 | 43 | assertArrayEquals(EXPECTED_OUTPUT, messageAuthenticationCode); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /jagged-scrypt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.exceptionfactory.jagged 9 | jagged 10 | 1.0.1-SNAPSHOT 11 | 12 | 13 | jagged-scrypt 14 | jagged-scrypt 15 | Jagged scrypt recipient support for age encryption 16 | https://github.com/exceptionfactory/jagged 17 | 18 | 19 | 20 | com.exceptionfactory.jagged 21 | jagged-api 22 | 23 | 24 | com.exceptionfactory.jagged 25 | jagged-framework 26 | 27 | 28 | org.junit.jupiter 29 | junit-jupiter-api 30 | test 31 | 32 | 33 | org.mockito 34 | mockito-core 35 | test 36 | 37 | 38 | org.mockito 39 | mockito-junit-jupiter 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-jar-plugin 49 | 50 | 51 | 52 | ${project.groupId}.scrypt 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /jagged-scrypt/src/main/java/com/exceptionfactory/jagged/scrypt/DerivedWrapKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.scrypt; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.CipherKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | 22 | /** 23 | * Abstraction for producing Wrap Key derived from scrypt parameters 24 | */ 25 | interface DerivedWrapKeyProducer { 26 | /** 27 | * Get Wrap Key 28 | * 29 | * @param salt Salt array of 16 bytes to derive scrypt S salt parameter 30 | * @param workFactor Work factor to derive scrypt N cost parameter 31 | * @return Recipient Stanza Cipher Key for decrypting File Key 32 | * @throws GeneralSecurityException Thrown on key derivation failures 33 | */ 34 | CipherKey getWrapKey(byte[] salt, int workFactor) throws GeneralSecurityException; 35 | } 36 | -------------------------------------------------------------------------------- /jagged-scrypt/src/main/java/com/exceptionfactory/jagged/scrypt/RecipientIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.scrypt; 17 | 18 | /** 19 | * scrypt Recipient Indicators for reading and writing Recipient Stanzas 20 | */ 21 | enum RecipientIndicator { 22 | /** Recipient Stanza Type */ 23 | STANZA_TYPE("scrypt"), 24 | 25 | /** Label prepended to random salt for scrypt function */ 26 | SALT_LABEL("age-encryption.org/v1/scrypt"); 27 | 28 | private final String indicator; 29 | 30 | RecipientIndicator(final String indicator) { 31 | this.indicator = indicator; 32 | } 33 | 34 | public String getIndicator() { 35 | return indicator; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-scrypt/src/test/java/com/exceptionfactory/jagged/scrypt/PasswordBasedKeyDerivationFunction2Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.scrypt; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.nio.charset.Charset; 21 | import java.nio.charset.StandardCharsets; 22 | import java.security.GeneralSecurityException; 23 | 24 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 25 | 26 | class PasswordBasedKeyDerivationFunction2Test { 27 | private static final Charset CHARACTER_SET = StandardCharsets.US_ASCII; 28 | 29 | private static final String PASSPHRASE = "passwd"; 30 | 31 | private static final String SALT = "salt"; 32 | 33 | private static final int DERIVED_KEY_LENGTH = 64; 34 | 35 | /** RFC 7914 Section 11 Input Test Vector for PBKDF2-HMAC-SHA-256 */ 36 | private static final String[] SINGLE_ITERATION_VECTOR = new String[]{ 37 | "55 ac 04 6e 56 e3 08 9f ec 16 91 c2 25 44 b6 05", 38 | "f9 41 85 21 6d de 04 65 e6 8b 9d 57 c2 0d ac bc", 39 | "49 ca 9c cc f1 79 b6 45 99 16 64 b3 9d 77 ef 31", 40 | "7c 71 b8 45 b1 e3 0b d5 09 11 20 41 d3 a1 97 83" 41 | }; 42 | 43 | @Test 44 | void testVector() throws GeneralSecurityException { 45 | final byte[] outputVector = ScryptFunctionTest.getOutputVector(SINGLE_ITERATION_VECTOR); 46 | 47 | final byte[] password = PASSPHRASE.getBytes(CHARACTER_SET); 48 | final byte[] salt = SALT.getBytes(CHARACTER_SET); 49 | final byte[] derivedKey = PasswordBasedKeyDerivationFunction2.getDerivedKey(password, salt, DERIVED_KEY_LENGTH); 50 | 51 | assertArrayEquals(outputVector, derivedKey); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /jagged-scrypt/src/test/java/com/exceptionfactory/jagged/scrypt/ScryptDerivedWrapKeyProducerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.scrypt; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.CipherKey; 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import java.nio.charset.StandardCharsets; 23 | import java.security.GeneralSecurityException; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertNotNull; 26 | 27 | class ScryptDerivedWrapKeyProducerTest { 28 | private static final byte[] PASSPHRASE = String.class.getName().getBytes(StandardCharsets.UTF_8); 29 | 30 | private static final byte[] EMPTY_SALT = new byte[]{}; 31 | 32 | private static final int MINIMUM_WORK_FACTOR = 2; 33 | 34 | private ScryptDerivedWrapKeyProducer producer; 35 | 36 | @BeforeEach 37 | void setProducer() { 38 | producer = new ScryptDerivedWrapKeyProducer(PASSPHRASE); 39 | } 40 | 41 | @Test 42 | void testGetWrapKey() throws GeneralSecurityException { 43 | final CipherKey wrapKey = producer.getWrapKey(EMPTY_SALT, MINIMUM_WORK_FACTOR); 44 | 45 | assertNotNull(wrapKey); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /jagged-scrypt/src/test/java/com/exceptionfactory/jagged/scrypt/ScryptRecipientStanzaWriterFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.scrypt; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanzaWriter; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.nio.charset.StandardCharsets; 22 | import java.security.Provider; 23 | import java.security.Security; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertNotNull; 26 | 27 | class ScryptRecipientStanzaWriterFactoryTest { 28 | private static final String ALGORITHM_FILTER = "Cipher.ChaCha20-Poly1305"; 29 | 30 | private static final byte[] PASSPHRASE = String.class.getName().getBytes(StandardCharsets.UTF_8); 31 | 32 | private static final int WORK_FACTOR = 14; 33 | 34 | @Test 35 | void testNewRecipientStanzaWriter() { 36 | final RecipientStanzaWriter recipientStanzaWriter = ScryptRecipientStanzaWriterFactory.newRecipientStanzaWriter(PASSPHRASE, WORK_FACTOR); 37 | 38 | assertNotNull(recipientStanzaWriter); 39 | } 40 | 41 | @Test 42 | void testNewRecipientStanzaWriterWithProvider() { 43 | final Provider provider = getProvider(); 44 | final RecipientStanzaWriter recipientStanzaWriter = ScryptRecipientStanzaWriterFactory.newRecipientStanzaWriter(PASSPHRASE, WORK_FACTOR, provider); 45 | 46 | assertNotNull(recipientStanzaWriter); 47 | } 48 | 49 | private Provider getProvider() { 50 | final Provider[] providers = Security.getProviders(ALGORITHM_FILTER); 51 | return providers[0]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /jagged-ssh/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.exceptionfactory.jagged 9 | jagged 10 | 1.0.1-SNAPSHOT 11 | 12 | 13 | jagged-ssh 14 | jagged-ssh 15 | Jagged SSH recipient support for age encryption 16 | https://github.com/exceptionfactory/jagged 17 | 18 | 19 | 20 | com.exceptionfactory.jagged 21 | jagged-api 22 | 23 | 24 | com.exceptionfactory.jagged 25 | jagged-framework 26 | 27 | 28 | org.junit.jupiter 29 | junit-jupiter-api 30 | test 31 | 32 | 33 | org.mockito 34 | mockito-core 35 | test 36 | 37 | 38 | org.mockito 39 | mockito-junit-jupiter 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-jar-plugin 49 | 50 | 51 | 52 | ${project.groupId}.ssh 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/Ed25519KeyIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | /** 19 | * Ed25519 Key indicator fields 20 | */ 21 | enum Ed25519KeyIndicator { 22 | /** Algorithm */ 23 | KEY_ALGORITHM("Ed25519"), 24 | 25 | /** Format */ 26 | KEY_FORMAT("RAW"); 27 | 28 | private final String indicator; 29 | 30 | Ed25519KeyIndicator(final String indicator) { 31 | this.indicator = indicator; 32 | } 33 | 34 | String getIndicator() { 35 | return indicator; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/Ed25519PublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.PublicKey; 19 | import java.util.Objects; 20 | 21 | /** 22 | * Ed25519 Public Key containing raw key bytes 23 | */ 24 | class Ed25519PublicKey implements PublicKey { 25 | private final byte[] encoded; 26 | 27 | /** 28 | * Ed25519 Public Key constructor with raw key bytes 29 | * 30 | * @param encoded raw byte array of 32 bytes 31 | */ 32 | Ed25519PublicKey(final byte[] encoded) { 33 | this.encoded = Objects.requireNonNull(encoded, "Encoded Key required"); 34 | } 35 | 36 | /** 37 | * Get algorithm describes the type of key 38 | * 39 | * @return Algorithm is Ed25519 40 | */ 41 | @Override 42 | public String getAlgorithm() { 43 | return Ed25519KeyIndicator.KEY_ALGORITHM.getIndicator(); 44 | } 45 | 46 | /** 47 | * Get format describes the encoded content bytes 48 | * 49 | * @return Encoded key format is RAW 50 | */ 51 | @Override 52 | public String getFormat() { 53 | return Ed25519KeyIndicator.KEY_FORMAT.getIndicator(); 54 | } 55 | 56 | /** 57 | * Get encoded key bytes consisting of original key 58 | * 59 | * @return encoded public key bytes 60 | */ 61 | @Override 62 | public byte[] getEncoded() { 63 | return encoded.clone(); 64 | } 65 | 66 | /** 67 | * Get string representation of key algorithm 68 | * 69 | * @return Key algorithm 70 | */ 71 | @Override 72 | public String toString() { 73 | return getAlgorithm(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/EllipticCurveKeyType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | /** 19 | * Elliptic Curve Key Type enumerates standard properties for Ed25519 and X25519 keys 20 | */ 21 | enum EllipticCurveKeyType { 22 | /** Ed25519 coordinate key of 32 bytes for twisted Edwards curve digital signature operations */ 23 | ED25519("Ed25519", 32), 24 | 25 | /** Curve25519 coordinate key of 32 bytes for X25519 key agreement operations */ 26 | X25519("X25519", 32); 27 | 28 | private final String algorithm; 29 | 30 | private final int keyLength; 31 | 32 | EllipticCurveKeyType(final String algorithm, final int keyLength) { 33 | this.algorithm = algorithm; 34 | this.keyLength = keyLength; 35 | } 36 | 37 | /** 38 | * Get algorithm name for Java Cryptography Architecture operations 39 | * 40 | * @return Algorithm name 41 | */ 42 | String getAlgorithm() { 43 | return algorithm; 44 | } 45 | 46 | /** 47 | * Get key length in bytes 48 | * 49 | * @return Key length in bytes 50 | */ 51 | int getKeyLength() { 52 | return keyLength; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/EmptyInputKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.MacKey; 19 | 20 | /** 21 | * Empty Input Key extension of Message Authentication Code Key for HKDF from salt key 22 | */ 23 | final class EmptyInputKey extends MacKey { 24 | private static final byte[] EMPTY = new byte[]{}; 25 | 26 | /** 27 | * Empty Input Key constructor 28 | * 29 | */ 30 | EmptyInputKey() { 31 | super(EMPTY, SshEd25519KeyType.EMPTY); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/KeyPairReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | import java.security.KeyPair; 21 | 22 | /** 23 | * Reader abstraction for loading Public and Private Key Pairs 24 | */ 25 | interface KeyPairReader { 26 | /** 27 | * Read Public and Private Key Pair 28 | * 29 | * @param inputBuffer Input Buffer to be read 30 | * @return Public and Private Key Pair 31 | * @throws GeneralSecurityException Thrown on failures to parse input buffer 32 | */ 33 | KeyPair read(ByteBuffer inputBuffer) throws GeneralSecurityException; 34 | } 35 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/KeySeparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | /** 19 | * Separator for encoded key elements 20 | */ 21 | enum KeySeparator { 22 | /** Line Feed Character */ 23 | LINE_FEED(10), 24 | 25 | /** Carriage Return Character */ 26 | CARRIAGE_RETURN(13); 27 | 28 | private final byte code; 29 | 30 | KeySeparator(final int code) { 31 | this.code = (byte) code; 32 | } 33 | 34 | byte getCode() { 35 | return code; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/OpenSshKeyIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.nio.charset.StandardCharsets; 19 | 20 | /** 21 | * OpenSSH Key Version 1 indicator fields 22 | */ 23 | enum OpenSshKeyIndicator { 24 | /** PEM Header */ 25 | HEADER("-----BEGIN OPENSSH PRIVATE KEY-----"), 26 | 27 | /** PEM Footer */ 28 | FOOTER("-----END OPENSSH PRIVATE KEY-----"), 29 | 30 | /** AUTH_MAGIC Header defined in openssh-portable/PROTOCOL.key */ 31 | MAGIC_HEADER("openssh-key-v1\0"), 32 | 33 | /** Cipher Name None indicating no encryption */ 34 | CIPHER_NAME_NONE("none"); 35 | 36 | private final byte[] indicator; 37 | 38 | OpenSshKeyIndicator(final String indicator) { 39 | this.indicator = indicator.getBytes(StandardCharsets.UTF_8); 40 | } 41 | 42 | byte[] getIndicator() { 43 | return indicator.clone(); 44 | } 45 | 46 | int getLength() { 47 | return indicator.length; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/PublicKeyFingerprintProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.GeneralSecurityException; 19 | 20 | /** 21 | * Abstraction for generating fingerprints of Public Keys 22 | */ 23 | interface PublicKeyFingerprintProducer { 24 | /** 25 | * Get fingerprint from marshalled Public Key bytes 26 | * 27 | * @param marshalledPublicKey Marshalled public key 28 | * @return Fingerprint label 29 | * @throws GeneralSecurityException Thrown on failures to generate fingerprints 30 | */ 31 | String getFingerprint(byte[] marshalledPublicKey) throws GeneralSecurityException; 32 | } 33 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/PublicKeyMarshaller.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.PublicKey; 19 | 20 | /** 21 | * SSH Public Key Marshaller abstraction for writing Public Key elements according to SSH wire format requirements 22 | * 23 | * @param Public Key Type 24 | */ 25 | interface PublicKeyMarshaller { 26 | /** 27 | * Get Public Key marshalled according to SSH wire format requirements 28 | * 29 | * @param publicKey Public Key to be marshalled 30 | * @return Byte array containing marshalled public key 31 | */ 32 | byte[] getMarshalledKey(T publicKey); 33 | } 34 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/PublicKeyReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | import java.security.PublicKey; 21 | 22 | /** 23 | * Reader abstraction for loading Public Keys 24 | * 25 | * @param Public Key Type 26 | */ 27 | interface PublicKeyReader { 28 | /** 29 | * Read Public Key 30 | * 31 | * @param inputBuffer Input Buffer to be read 32 | * @return Public Key 33 | * @throws GeneralSecurityException Thrown on failures to parse input buffer 34 | */ 35 | T read(ByteBuffer inputBuffer) throws GeneralSecurityException; 36 | } 37 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/RsaPublicKeyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.GeneralSecurityException; 19 | import java.security.interfaces.RSAPrivateCrtKey; 20 | import java.security.interfaces.RSAPublicKey; 21 | 22 | /** 23 | * Abstraction for deriving RSA Public Keys from RSA Private Keys 24 | */ 25 | interface RsaPublicKeyFactory { 26 | /** 27 | * Get RSA Public Key 28 | * 29 | * @param privateKey RSA Private Key 30 | * @return RSA Public Key 31 | * @throws GeneralSecurityException Thrown on failure to convert private key to public key 32 | */ 33 | RSAPublicKey getPublicKey(RSAPrivateCrtKey privateKey) throws GeneralSecurityException; 34 | } 35 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SharedSecretKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.SharedSecretKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | import java.security.PublicKey; 22 | 23 | /** 24 | * Abstraction around javax.crypto.KeyAgreement for Shared Secret Key production 25 | */ 26 | interface SharedSecretKeyProducer { 27 | /** 28 | * Get Shared Secret Key using provided Public Key 29 | * 30 | * @param publicKey Public Key 31 | * @return Shared Secret Key 32 | * @throws GeneralSecurityException Thrown on failures to produce Shared Secret Key 33 | */ 34 | SharedSecretKey getSharedSecretKey(PublicKey publicKey) throws GeneralSecurityException; 35 | } 36 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SharedWrapKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.CipherKey; 19 | import com.exceptionfactory.jagged.framework.crypto.SharedSecretKey; 20 | 21 | import java.security.GeneralSecurityException; 22 | import java.security.PublicKey; 23 | 24 | /** 25 | * Abstraction for producing Wrap Key from Shared Secret Key using HMAC-based Extract-and-Expand Key Derivation Function described in RFC 5869 26 | */ 27 | interface SharedWrapKeyProducer { 28 | /** 29 | * Get Wrap Key using shared secret key and ephemeral public key 30 | * 31 | * @param sharedSecretKey Shared Secret Key 32 | * @param ephemeralPublicKey Ephemeral Public Key from Recipient Stanza Arguments 33 | * @return Recipient Stanza Cipher Key for decrypting File Key 34 | * @throws GeneralSecurityException Thrown on key derivation failures 35 | */ 36 | CipherKey getWrapKey(SharedSecretKey sharedSecretKey, PublicKey ephemeralPublicKey) throws GeneralSecurityException; 37 | } 38 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519DerivedKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.MacKey; 19 | 20 | /** 21 | * SSH Ed25519 Derived Key containing results of HKDF-SHA-256 from marshalled SSH public key 22 | */ 23 | final class SshEd25519DerivedKey extends MacKey { 24 | /** 25 | * SSH Ed25519 Derived Public Key constructor with required key 26 | * 27 | * @param key Derived Key consisting of 32 bytes 28 | */ 29 | SshEd25519DerivedKey(final byte[] key) { 30 | super(key, SshEd25519KeyType.DERIVED); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519KeyType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.CryptographicKeyDescription; 19 | 20 | /** 21 | * SSH Ed25519 Key Type references for construction and validation 22 | */ 23 | enum SshEd25519KeyType implements CryptographicKeyDescription { 24 | /** Derived Secret Key */ 25 | DERIVED(32), 26 | 27 | /** Empty Input Key for HKDF-SHA-256 */ 28 | EMPTY(0), 29 | 30 | /** Marshalled Public Key */ 31 | MARSHALLED(51); 32 | 33 | private final int keyLength; 34 | 35 | SshEd25519KeyType(final int keyLength) { 36 | this.keyLength = keyLength; 37 | } 38 | 39 | /** 40 | * Get key length in bytes 41 | * 42 | * @return Key length in bytes 43 | */ 44 | @Override 45 | public int getKeyLength() { 46 | return keyLength; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519MarshalledKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.MacKey; 19 | 20 | /** 21 | * SSH Ed25519 Marshalled Public Key containing marshalled SSH public key bytes 22 | */ 23 | final class SshEd25519MarshalledKey extends MacKey { 24 | /** 25 | * SSH Ed25519 Marshalled Public Key constructor with required key 26 | * 27 | * @param key Marshalled Key consisting of 51 bytes 28 | */ 29 | SshEd25519MarshalledKey(final byte[] key) { 30 | super(key, SshEd25519KeyType.MARSHALLED); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519OpenSshKeyPairReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | import java.security.KeyPair; 21 | 22 | /** 23 | * SSH Ed25519 implementation reads the Ed25519 Private Key portion of an OpenSSH Version 1 Key 24 | */ 25 | class SshEd25519OpenSshKeyPairReader extends OpenSshKeyByteBufferReader { 26 | /** 27 | * Read Ed25519 Key Pair from bytes 28 | * 29 | * @param buffer Input Buffer to be read 30 | * @return Ed25519 Public and Private Key Pair 31 | * @throws GeneralSecurityException Thrown on failures to parse input buffer 32 | */ 33 | @Override 34 | public KeyPair read(final ByteBuffer buffer) throws GeneralSecurityException { 35 | final byte[] publicKeyBlock = readBlock(buffer); 36 | final Ed25519PublicKey publicKey = new Ed25519PublicKey(publicKeyBlock); 37 | 38 | final byte[] privatePublicKeyBlock = readBlock(buffer); 39 | final byte[] privateKeySeed = new byte[EllipticCurveKeyType.ED25519.getKeyLength()]; 40 | System.arraycopy(privatePublicKeyBlock, 0, privateKeySeed, 0, privateKeySeed.length); 41 | 42 | final Ed25519PrivateKey privateKey = new Ed25519PrivateKey(privateKeySeed); 43 | return new KeyPair(publicKey, privateKey); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519PublicKeyMarshaller.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.nio.charset.StandardCharsets; 20 | import java.security.PublicKey; 21 | import java.util.Objects; 22 | 23 | /** 24 | * SSH Ed25519 implementation of Public Key Marshaller writes the Ed25519 public key along with the key algorithm 25 | */ 26 | class SshEd25519PublicKeyMarshaller implements PublicKeyMarshaller { 27 | private static final byte[] SSH_ED25519_ALGORITHM = SshEd25519RecipientIndicator.STANZA_TYPE.getIndicator().getBytes(StandardCharsets.UTF_8); 28 | 29 | private static final int BUFFER_SIZE = 128; 30 | 31 | /** 32 | * Get Public Key marshalled according to SSH wire format requirements 33 | * 34 | * @param publicKey Ed25519 Public Key to be marshalled 35 | * @return Byte array containing marshalled public key with ssh-ed25519 algorithm and public key 36 | */ 37 | @Override 38 | public byte[] getMarshalledKey(final PublicKey publicKey) { 39 | Objects.requireNonNull(publicKey, "Public Key required"); 40 | 41 | final ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); 42 | writeBytes(buffer, SSH_ED25519_ALGORITHM); 43 | writeBytes(buffer, publicKey.getEncoded()); 44 | 45 | final byte[] marshalledKey = new byte[buffer.position()]; 46 | buffer.flip(); 47 | buffer.get(marshalledKey); 48 | return marshalledKey; 49 | } 50 | 51 | private void writeBytes(final ByteBuffer buffer, final byte[] bytes) { 52 | buffer.putInt(bytes.length); 53 | buffer.put(bytes); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519PublicKeyReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.security.GeneralSecurityException; 20 | import java.security.InvalidKeyException; 21 | 22 | /** 23 | * SSH Ed25519 Public Key Reader implementation based on ssh-ed25519 format described in RFC 8709 Section 4 24 | */ 25 | class SshEd25519PublicKeyReader extends SshPublicKeyReader { 26 | /** 27 | * SSH Ed25519 Public Key Reader constructor configures the expected ssh-ed25519 algorithm 28 | */ 29 | SshEd25519PublicKeyReader() { 30 | super(SshEd25519RecipientIndicator.STANZA_TYPE.getIndicator()); 31 | } 32 | 33 | /** 34 | * Read Ed25519 Public Key from block of 32 bytes 35 | * 36 | * @param decodedBuffer Buffer of bytes decoded from Base64 public key 37 | * @return Ed25519 Public Key 38 | * @throws GeneralSecurityException Thrown when block length not equal to expected key length 39 | */ 40 | @Override 41 | protected Ed25519PublicKey readPublicKey(final ByteBuffer decodedBuffer) throws GeneralSecurityException { 42 | final byte[] block = readBlock(decodedBuffer); 43 | if (EllipticCurveKeyType.ED25519.getKeyLength() == block.length) { 44 | return new Ed25519PublicKey(block); 45 | } else { 46 | final String message = String.format("Public key length [%d] not expected [%d]", block.length, EllipticCurveKeyType.ED25519.getKeyLength()); 47 | throw new InvalidKeyException(message); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshEd25519RecipientIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | /** 19 | * SSH Ed25519 Recipient Indicators for reading and writing Recipient Stanzas 20 | */ 21 | enum SshEd25519RecipientIndicator { 22 | /** SSH Ed25519 Recipient Stanza Type */ 23 | STANZA_TYPE("ssh-ed25519"), 24 | 25 | /** Key Information used for HKDF-SHA-256 */ 26 | KEY_INFORMATION("age-encryption.org/v1/ssh-ed25519"); 27 | 28 | private final String indicator; 29 | 30 | SshEd25519RecipientIndicator(final String indicator) { 31 | this.indicator = indicator; 32 | } 33 | 34 | public String getIndicator() { 35 | return indicator; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshKeyType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | /** 19 | * SSH Key Types 20 | */ 21 | enum SshKeyType { 22 | DSS("ssh-dss"), 23 | 24 | ED25519("ssh-ed25519"), 25 | 26 | RSA("ssh-rsa"); 27 | 28 | private final String keyType; 29 | 30 | SshKeyType(final String keyType) { 31 | this.keyType = keyType; 32 | } 33 | 34 | String getKeyType() { 35 | return keyType; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshRsaRecipientIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | /** 19 | * SSH RSA Recipient Indicators for reading and writing Recipient Stanzas 20 | */ 21 | enum SshRsaRecipientIndicator { 22 | /** SSH RSA Recipient Stanza Type */ 23 | STANZA_TYPE("ssh-rsa"), 24 | 25 | /** Label used for RSA OAEP encryption */ 26 | ENCODING_LABEL("age-encryption.org/v1/ssh-rsa"); 27 | 28 | private final String indicator; 29 | 30 | SshRsaRecipientIndicator(final String indicator) { 31 | this.indicator = indicator; 32 | } 33 | 34 | public String getIndicator() { 35 | return indicator; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/SshRsaRecipientStanza.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanza; 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.Objects; 23 | 24 | /** 25 | * SSH RSA Recipient Stanza with standard type and arguments containing encoded public key fingerprint 26 | */ 27 | class SshRsaRecipientStanza implements RecipientStanza { 28 | private final List arguments; 29 | 30 | private final byte[] body; 31 | 32 | SshRsaRecipientStanza(final String keyFingerprint, final byte[] body) { 33 | Objects.requireNonNull(keyFingerprint, "Key fingerprint required"); 34 | this.arguments = Collections.singletonList(keyFingerprint); 35 | this.body = Objects.requireNonNull(body, "Stanza Body required"); 36 | } 37 | 38 | /** 39 | * Get Recipient Stanza Type returns ssh-rsa 40 | * 41 | * @return SSH RSA Type 42 | */ 43 | @Override 44 | public String getType() { 45 | return SshRsaRecipientIndicator.STANZA_TYPE.getIndicator(); 46 | } 47 | 48 | /** 49 | * Get Recipient Stanza Arguments containing the key fingerprint 50 | * 51 | * @return Recipient Stanza Arguments 52 | */ 53 | @Override 54 | public List getArguments() { 55 | return arguments; 56 | } 57 | 58 | /** 59 | * Get Recipient Stanza Body containing encrypted File Key 60 | * 61 | * @return Encrypted File Key body 62 | */ 63 | @Override 64 | public byte[] getBody() { 65 | return body.clone(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/StandardRsaPublicKeyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.GeneralSecurityException; 19 | import java.security.KeyFactory; 20 | import java.security.interfaces.RSAPrivateCrtKey; 21 | import java.security.interfaces.RSAPublicKey; 22 | import java.security.spec.RSAPublicKeySpec; 23 | import java.util.Objects; 24 | 25 | /** 26 | * Standard implementation of RSA Public Key Factory 27 | */ 28 | class StandardRsaPublicKeyFactory implements RsaPublicKeyFactory { 29 | /** 30 | * Get RSA Public Key 31 | * 32 | * @param privateKey RSA Private Key 33 | * @return RSA Public Key 34 | * @throws GeneralSecurityException Thrown on failure to convert private key to public key 35 | */ 36 | @Override 37 | public RSAPublicKey getPublicKey(final RSAPrivateCrtKey privateKey) throws GeneralSecurityException { 38 | Objects.requireNonNull(privateKey, "RSA Private Key required"); 39 | 40 | final KeyFactory keyFactory = KeyFactory.getInstance(privateKey.getAlgorithm()); 41 | final RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(privateKey.getModulus(), privateKey.getPublicExponent()); 42 | return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/X25519BasePointPublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.PublicKey; 19 | 20 | /** 21 | * Curve25519 Base Point Public Key as described in RFC 7748 Section 4.1 22 | */ 23 | class X25519BasePointPublicKey implements PublicKey { 24 | private static final byte BASE_POINT = 9; 25 | 26 | private static final String FORMAT = "RAW"; 27 | 28 | private static final byte[] BASE_POINT_PUBLIC_KEY = new byte[EllipticCurveKeyType.X25519.getKeyLength()]; 29 | 30 | static { 31 | BASE_POINT_PUBLIC_KEY[0] = BASE_POINT; 32 | } 33 | 34 | /** 35 | * Get algorithm returns X25519 for Key Agreement operations 36 | * 37 | * @return X25519 algorithm 38 | */ 39 | @Override 40 | public String getAlgorithm() { 41 | return EllipticCurveKeyType.X25519.getAlgorithm(); 42 | } 43 | 44 | /** 45 | * Get format returns RAW 46 | * 47 | * @return RAW format 48 | */ 49 | @Override 50 | public String getFormat() { 51 | return FORMAT; 52 | } 53 | 54 | /** 55 | * Get encoded Base Point Public Key as 32 bytes with leading 9 following RFC 7748 Section 4.1 56 | * 57 | * @return Base Point Public Key as 32 bytes 58 | */ 59 | @Override 60 | public byte[] getEncoded() { 61 | return BASE_POINT_PUBLIC_KEY.clone(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /jagged-ssh/src/main/java/com/exceptionfactory/jagged/ssh/X25519KeyPairGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.KeyPairGenerator; 19 | import java.security.NoSuchAlgorithmException; 20 | import java.security.Provider; 21 | import java.util.Objects; 22 | 23 | /** 24 | * Factory abstraction for instances of javax.security.KeyPairGenerator with X25519 25 | */ 26 | final class X25519KeyPairGeneratorFactory { 27 | private final Provider provider; 28 | 29 | /** 30 | * Key Pair Generator Factory default constructor uses the system default Security Provider configuration 31 | */ 32 | X25519KeyPairGeneratorFactory() { 33 | provider = null; 34 | } 35 | 36 | /** 37 | * Key Pair Generator Factory constructor with support for custom Security Provider 38 | * 39 | * @param provider Security Provider supporting X25519 40 | */ 41 | X25519KeyPairGeneratorFactory(final Provider provider) { 42 | this.provider = Objects.requireNonNull(provider, "Provider required"); 43 | } 44 | 45 | KeyPairGenerator getKeyPairGenerator() throws NoSuchAlgorithmException { 46 | final KeyPairGenerator keyPairGenerator; 47 | 48 | if (provider == null) { 49 | keyPairGenerator = KeyPairGenerator.getInstance(EllipticCurveKeyType.X25519.getAlgorithm()); 50 | } else { 51 | keyPairGenerator = KeyPairGenerator.getInstance(EllipticCurveKeyType.X25519.getAlgorithm(), provider); 52 | } 53 | 54 | return keyPairGenerator; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/Ed25519KeyPairProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.codec.CanonicalBase64; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | 22 | final class Ed25519KeyPairProvider { 23 | 24 | private static final String PUBLIC_KEY_ENCODED = "HURauP50ZGJDuUkn6tjy/PGQWmTR2FFyDYXP071GP3U"; 25 | 26 | private static final String PRIVATE_KEY_ENCODED = "nWNC6CoPYFBs1dSBWSYPFjQ4+APPoH/3DQoB2kCairA"; 27 | 28 | private static final CanonicalBase64.Decoder DECODER = CanonicalBase64.getDecoder(); 29 | 30 | private static Ed25519PublicKey publicKey; 31 | 32 | private static Ed25519PrivateKey privateKey; 33 | 34 | private Ed25519KeyPairProvider() { 35 | 36 | } 37 | 38 | static synchronized Ed25519PublicKey getPublicKey() { 39 | if (publicKey == null) { 40 | setKeyPair(); 41 | } 42 | return publicKey; 43 | } 44 | 45 | static synchronized Ed25519PrivateKey getPrivateKey() { 46 | if (privateKey == null) { 47 | setKeyPair(); 48 | } 49 | return privateKey; 50 | } 51 | 52 | private static void setKeyPair() { 53 | final byte[] publicKeyEncoded = DECODER.decode(PUBLIC_KEY_ENCODED.getBytes(StandardCharsets.UTF_8)); 54 | publicKey = new Ed25519PublicKey(publicKeyEncoded); 55 | 56 | final byte[] privateKeyEncoded = DECODER.decode(PRIVATE_KEY_ENCODED.getBytes(StandardCharsets.UTF_8)); 57 | privateKey = new Ed25519PrivateKey(privateKeyEncoded); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/RsaKeyPairProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import java.security.KeyPair; 19 | import java.security.KeyPairGenerator; 20 | import java.security.NoSuchAlgorithmException; 21 | import java.security.interfaces.RSAPrivateCrtKey; 22 | import java.security.interfaces.RSAPublicKey; 23 | 24 | final class RsaKeyPairProvider { 25 | private static final String KEY_ALGORITHM = "RSA"; 26 | 27 | private static final int KEY_SIZE = 4096; 28 | 29 | private static RSAPublicKey rsaPublicKey; 30 | 31 | private static RSAPrivateCrtKey rsaPrivateCrtKey; 32 | 33 | private RsaKeyPairProvider() { 34 | 35 | } 36 | 37 | static synchronized RSAPublicKey getRsaPublicKey() throws NoSuchAlgorithmException { 38 | if (rsaPublicKey == null) { 39 | setKeyPair(); 40 | } 41 | return rsaPublicKey; 42 | } 43 | 44 | static synchronized RSAPrivateCrtKey getRsaPrivateCrtKey() throws NoSuchAlgorithmException { 45 | if (rsaPrivateCrtKey == null) { 46 | setKeyPair(); 47 | } 48 | return rsaPrivateCrtKey; 49 | } 50 | 51 | private static void setKeyPair() throws NoSuchAlgorithmException { 52 | final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); 53 | keyPairGenerator.initialize(KEY_SIZE); 54 | final KeyPair keyPair = keyPairGenerator.generateKeyPair(); 55 | rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); 56 | rsaPrivateCrtKey = (RSAPrivateCrtKey) keyPair.getPrivate(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/SshEd25519PublicKeyMarshallerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import org.junit.jupiter.api.BeforeEach; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 24 | import static org.junit.jupiter.api.Assertions.assertEquals; 25 | import static org.junit.jupiter.api.Assertions.assertNotNull; 26 | 27 | class SshEd25519PublicKeyMarshallerTest { 28 | private static final byte[] SSH_ED25519_ALGORITHM_SERIALIZED = {0, 0, 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57}; 29 | 30 | private SshEd25519PublicKeyMarshaller marshaller; 31 | 32 | @BeforeEach 33 | void setMarshaller() { 34 | marshaller = new SshEd25519PublicKeyMarshaller(); 35 | } 36 | 37 | @Test 38 | void testGetMarshalledKey() { 39 | final Ed25519PublicKey publicKey = Ed25519KeyPairProvider.getPublicKey(); 40 | final byte[] marshalledKey = marshaller.getMarshalledKey(publicKey); 41 | 42 | assertNotNull(marshalledKey); 43 | assertEquals(SshEd25519KeyType.MARSHALLED.getKeyLength(), marshalledKey.length); 44 | 45 | final byte[] algorithm = Arrays.copyOfRange(marshalledKey, 0, SSH_ED25519_ALGORITHM_SERIALIZED.length); 46 | assertArrayEquals(SSH_ED25519_ALGORITHM_SERIALIZED, algorithm); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/StandardPublicKeyFingerprintProducerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import org.junit.jupiter.api.BeforeEach; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.security.GeneralSecurityException; 22 | 23 | import static org.junit.jupiter.api.Assertions.assertEquals; 24 | import static org.junit.jupiter.api.Assertions.assertNotNull; 25 | 26 | class StandardPublicKeyFingerprintProducerTest { 27 | private static final byte[] PUBLIC_KEY = {0, 1, 2, 3}; 28 | 29 | private static final String EXPECTED_FINGERPRINT = "BU7ewQ"; 30 | 31 | private StandardPublicKeyFingerprintProducer fingerprintProducer; 32 | 33 | @BeforeEach 34 | void setFingerprintProducer() { 35 | fingerprintProducer = new StandardPublicKeyFingerprintProducer(); 36 | } 37 | 38 | @Test 39 | void testGetFingerprint() throws GeneralSecurityException { 40 | final String fingerprint = fingerprintProducer.getFingerprint(PUBLIC_KEY); 41 | 42 | assertNotNull(fingerprint); 43 | assertEquals(EXPECTED_FINGERPRINT, fingerprint); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/X25519BasePointPublicKeyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | 22 | class X25519BasePointPublicKeyTest { 23 | private static final int BASE_POINT = 9; 24 | 25 | private static final String FORMAT = "RAW"; 26 | 27 | @Test 28 | void testGetAlgorithm() { 29 | final X25519BasePointPublicKey basePointPublicKey = new X25519BasePointPublicKey(); 30 | 31 | assertEquals(EllipticCurveKeyType.X25519.getAlgorithm(), basePointPublicKey.getAlgorithm()); 32 | } 33 | 34 | @Test 35 | void testGetFormat() { 36 | final X25519BasePointPublicKey basePointPublicKey = new X25519BasePointPublicKey(); 37 | 38 | assertEquals(FORMAT, basePointPublicKey.getFormat()); 39 | } 40 | 41 | @Test 42 | void testGetEncoded() { 43 | final X25519BasePointPublicKey basePointPublicKey = new X25519BasePointPublicKey(); 44 | 45 | final byte[] encoded = basePointPublicKey.getEncoded(); 46 | assertEquals(EllipticCurveKeyType.X25519.getKeyLength(), encoded.length); 47 | assertEquals(BASE_POINT, encoded[0]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/X25519KeyPairGeneratorFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.security.KeyPairGenerator; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.Provider; 23 | import java.security.Security; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertNotNull; 26 | 27 | class X25519KeyPairGeneratorFactoryTest { 28 | private static final String ALGORITHM_FILTER = String.format("KeyPairGenerator.%s", EllipticCurveKeyType.X25519.getAlgorithm()); 29 | 30 | @Test 31 | void testGetKeyPairGenerator() throws NoSuchAlgorithmException { 32 | final X25519KeyPairGeneratorFactory keyPairGeneratorFactory = new X25519KeyPairGeneratorFactory(); 33 | final KeyPairGenerator keyPairGenerator = keyPairGeneratorFactory.getKeyPairGenerator(); 34 | 35 | assertNotNull(keyPairGenerator); 36 | } 37 | 38 | @Test 39 | void testGetKeyPairGeneratorWithProvider() throws NoSuchAlgorithmException { 40 | final Provider provider = getProvider(); 41 | final X25519KeyPairGeneratorFactory keyPairGeneratorFactory = new X25519KeyPairGeneratorFactory(provider); 42 | final KeyPairGenerator keyPairGenerator = keyPairGeneratorFactory.getKeyPairGenerator(); 43 | 44 | assertNotNull(keyPairGenerator); 45 | } 46 | 47 | private Provider getProvider() { 48 | final Provider[] providers = Security.getProviders(ALGORITHM_FILTER); 49 | return providers[0]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jagged-ssh/src/test/java/com/exceptionfactory/jagged/ssh/X25519SharedSecretKeyProducerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.ssh; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.SharedSecretKey; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.security.GeneralSecurityException; 22 | import java.security.KeyPair; 23 | import java.security.KeyPairGenerator; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 26 | 27 | class X25519SharedSecretKeyProducerTest { 28 | 29 | @Test 30 | void testGetSharedSecretKey() throws GeneralSecurityException { 31 | final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(EllipticCurveKeyType.X25519.getAlgorithm()); 32 | 33 | final KeyPair senderKeyPair = keyPairGenerator.generateKeyPair(); 34 | final KeyPair recipientKeyPair = keyPairGenerator.generateKeyPair(); 35 | 36 | final X25519KeyAgreementFactory keyAgreementFactory = new X25519KeyAgreementFactory(); 37 | 38 | final X25519SharedSecretKeyProducer senderProducer = new X25519SharedSecretKeyProducer(senderKeyPair.getPrivate(), keyAgreementFactory); 39 | final SharedSecretKey senderSharedSecretKey = senderProducer.getSharedSecretKey(recipientKeyPair.getPublic()); 40 | 41 | final X25519SharedSecretKeyProducer recipientProducer = new X25519SharedSecretKeyProducer(recipientKeyPair.getPrivate(), keyAgreementFactory); 42 | final SharedSecretKey recipientSharedSecretKey = recipientProducer.getSharedSecretKey(senderKeyPair.getPublic()); 43 | 44 | assertArrayEquals(senderSharedSecretKey.getEncoded(), recipientSharedSecretKey.getEncoded()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jagged-test/src/test/java/com/exceptionfactory/jagged/test/CommunityCryptographyExpectation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.test; 17 | 18 | import com.exceptionfactory.jagged.PayloadException; 19 | import com.exceptionfactory.jagged.UnsupportedRecipientStanzaException; 20 | import com.exceptionfactory.jagged.framework.armor.ArmoredDecodingException; 21 | 22 | import java.security.GeneralSecurityException; 23 | import java.security.SignatureException; 24 | 25 | /** 26 | * Community Cryptography Test Vector expected results 27 | */ 28 | enum CommunityCryptographyExpectation { 29 | SUCCESS("success", null), 30 | 31 | NO_MATCH("no match", UnsupportedRecipientStanzaException.class), 32 | 33 | HMAC_FAILURE("HMAC failure", SignatureException.class), 34 | 35 | HEADER_FAILURE("header failure", GeneralSecurityException.class), 36 | 37 | PAYLOAD_FAILURE("payload failure", PayloadException.class), 38 | 39 | ARMOR_FAILURE("armor failure", ArmoredDecodingException.class); 40 | 41 | private final String label; 42 | 43 | private final Class exceptionClass; 44 | 45 | CommunityCryptographyExpectation(final String label, final Class exceptionClass) { 46 | this.label = label; 47 | this.exceptionClass = exceptionClass; 48 | } 49 | 50 | String getLabel() { 51 | return label; 52 | } 53 | 54 | Class getExceptionClass() { 55 | return exceptionClass; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /jagged-test/src/test/java/com/exceptionfactory/jagged/test/CommunityCryptographyProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.test; 17 | 18 | /** 19 | * Standard property names from age encryption Community Cryptography Test Vectors 20 | */ 21 | enum CommunityCryptographyProperty { 22 | ARMORED("armored"), 23 | 24 | PAYLOAD("payload"), 25 | 26 | IDENTITY("identity"), 27 | 28 | PASSPHRASE("passphrase"), 29 | 30 | EXPECT("expect"); 31 | 32 | private final String property; 33 | 34 | CommunityCryptographyProperty(final String property) { 35 | this.property = property; 36 | } 37 | 38 | String getProperty() { 39 | return property; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jagged-test/src/test/resources/java.security: -------------------------------------------------------------------------------- 1 | security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider 2 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/BasePointPublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import java.security.PublicKey; 19 | 20 | /** 21 | * Curve25519 Base Point Public Key as described in RFC 7748 Section 4.1 22 | */ 23 | class BasePointPublicKey implements PublicKey { 24 | private static final byte BASE_POINT = 9; 25 | 26 | private static final String FORMAT = "RAW"; 27 | 28 | private static final byte[] BASE_POINT_PUBLIC_KEY = new byte[RecipientKeyType.X25519.getKeyLength()]; 29 | 30 | static { 31 | BASE_POINT_PUBLIC_KEY[0] = BASE_POINT; 32 | } 33 | 34 | /** 35 | * Get algorithm returns X25519 for Key Agreement operations 36 | * 37 | * @return X25519 algorithm 38 | */ 39 | @Override 40 | public String getAlgorithm() { 41 | return RecipientIndicator.KEY_ALGORITHM.getIndicator(); 42 | } 43 | 44 | /** 45 | * Get format returns RAW 46 | * 47 | * @return RAW format 48 | */ 49 | @Override 50 | public String getFormat() { 51 | return FORMAT; 52 | } 53 | 54 | /** 55 | * Get encoded Base Point Public Key as 32 bytes with leading 9 following RFC 7748 Section 4.1 56 | * 57 | * @return Base Point Public Key as 32 bytes 58 | */ 59 | @Override 60 | public byte[] getEncoded() { 61 | return BASE_POINT_PUBLIC_KEY.clone(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/IdentityIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | /** 19 | * X2559 Identity Indicators for reading and writing Identities 20 | */ 21 | enum IdentityIndicator { 22 | /** Bech32 Encoded Private Human-Readable Part */ 23 | PRIVATE_KEY_HUMAN_READABLE_PART("AGE-SECRET-KEY-"); 24 | 25 | private final String indicator; 26 | 27 | IdentityIndicator(final String indicator) { 28 | this.indicator = indicator; 29 | } 30 | 31 | public String getIndicator() { 32 | return indicator; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/KeyPairGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import java.security.KeyPairGenerator; 19 | import java.security.NoSuchAlgorithmException; 20 | import java.security.Provider; 21 | import java.util.Objects; 22 | 23 | /** 24 | * Factory abstraction for instances of javax.security.KeyPairGenerator with X25519 25 | */ 26 | final class KeyPairGeneratorFactory { 27 | private final Provider provider; 28 | 29 | /** 30 | * Key Pair Generator Factory default constructor uses the system default Security Provider configuration 31 | */ 32 | KeyPairGeneratorFactory() { 33 | provider = null; 34 | } 35 | 36 | /** 37 | * Key Pair Generator Factory constructor with support for custom Security Provider 38 | * 39 | * @param provider Security Provider supporting X25519 40 | */ 41 | KeyPairGeneratorFactory(final Provider provider) { 42 | this.provider = Objects.requireNonNull(provider, "Provider required"); 43 | } 44 | 45 | KeyPairGenerator getKeyPairGenerator() throws NoSuchAlgorithmException { 46 | final KeyPairGenerator keyPairGenerator; 47 | 48 | if (provider == null) { 49 | keyPairGenerator = KeyPairGenerator.getInstance(RecipientIndicator.KEY_ALGORITHM.getIndicator()); 50 | } else { 51 | keyPairGenerator = KeyPairGenerator.getInstance(RecipientIndicator.KEY_ALGORITHM.getIndicator(), provider); 52 | } 53 | 54 | return keyPairGenerator; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/RecipientIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | /** 19 | * X2559 Recipient Indicators for reading and writing Recipient Stanzas 20 | */ 21 | enum RecipientIndicator { 22 | /** Bech32 Encoded Public Key Human-Readable Part */ 23 | PUBLIC_KEY_HUMAN_READABLE_PART("age"), 24 | 25 | /** Recipient Stanza Type */ 26 | STANZA_TYPE("X25519"), 27 | 28 | /** Key Algorithm for cryptographic operations with Diffie-Hellman Key Exchange using Curve25519 */ 29 | KEY_ALGORITHM("X25519"), 30 | 31 | /** HKDF-SHA-256 Key Information */ 32 | KEY_INFORMATION("age-encryption.org/v1/X25519"); 33 | 34 | private final String indicator; 35 | 36 | RecipientIndicator(final String indicator) { 37 | this.indicator = indicator; 38 | } 39 | 40 | public String getIndicator() { 41 | return indicator; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/RecipientKeyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import java.security.GeneralSecurityException; 19 | import java.security.PrivateKey; 20 | import java.security.PublicKey; 21 | 22 | /** 23 | * Abstraction for producing Java Cryptography Public Key and Private Key objects from binary representations 24 | */ 25 | interface RecipientKeyFactory { 26 | /** 27 | * Get Public Key from encoded binary 28 | * 29 | * @param publicKeyEncoded Encoded public key 30 | * @return Public Key 31 | * @throws GeneralSecurityException Thrown on key processing failures 32 | */ 33 | PublicKey getPublicKey(byte[] publicKeyEncoded) throws GeneralSecurityException; 34 | 35 | /** 36 | * Get Private Key from encoded binary 37 | * 38 | * @param privateKeyEncoded Encoded private key 39 | * @return Private Key 40 | * @throws GeneralSecurityException Thrown on key processing failures 41 | */ 42 | PrivateKey getPrivateKey(byte[] privateKeyEncoded) throws GeneralSecurityException; 43 | } 44 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/RecipientKeyType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | /** 19 | * Recipient Key Type enumerates standard properties for X25519 keys 20 | */ 21 | enum RecipientKeyType { 22 | /** Curve25519 coordinate key of 32 bytes for X25519 key agreement operations */ 23 | X25519(32); 24 | 25 | private final int keyLength; 26 | 27 | RecipientKeyType(final int keyLength) { 28 | this.keyLength = keyLength; 29 | } 30 | 31 | /** 32 | * Get key length in bytes 33 | * 34 | * @return Key length in bytes 35 | */ 36 | public int getKeyLength() { 37 | return keyLength; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/SharedSecretKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.SharedSecretKey; 19 | 20 | import java.security.GeneralSecurityException; 21 | import java.security.PublicKey; 22 | 23 | /** 24 | * Abstraction around javax.crypto.KeyAgreement for Shared Secret Key production 25 | */ 26 | interface SharedSecretKeyProducer { 27 | /** 28 | * Get Shared Secret Key using provided Public Key 29 | * 30 | * @param publicKey Public Key 31 | * @return Shared Secret Key 32 | * @throws GeneralSecurityException Thrown on failures to produced Shared Secret Key 33 | */ 34 | SharedSecretKey getSharedSecretKey(PublicKey publicKey) throws GeneralSecurityException; 35 | } 36 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/SharedWrapKeyProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.CipherKey; 19 | import com.exceptionfactory.jagged.framework.crypto.SharedSecretKey; 20 | 21 | import java.security.GeneralSecurityException; 22 | import java.security.PublicKey; 23 | 24 | /** 25 | * Abstraction for producing Wrap Key from Shared Secret Key using HMAC-based Extract-and-Expand Key Derivation Function described in RFC 5869 26 | */ 27 | interface SharedWrapKeyProducer { 28 | /** 29 | * Get Wrap Key using shared secret key and ephemeral public key 30 | * 31 | * @param sharedSecretKey Shared Secret Key 32 | * @param ephemeralPublicKey Ephemeral Public Key decoded from Recipient Stanza Arguments 33 | * @return Recipient Stanza Cipher Key for decrypting File Key 34 | * @throws GeneralSecurityException Thrown on key derivation failures 35 | */ 36 | CipherKey getWrapKey(SharedSecretKey sharedSecretKey, PublicKey ephemeralPublicKey) throws GeneralSecurityException; 37 | } 38 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/X25519KeyFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import java.security.GeneralSecurityException; 19 | import java.security.KeyFactory; 20 | import java.security.Provider; 21 | 22 | /** 23 | * X25519 Key Factory capable of translating Key Specifications encoded according to age-encryption.org to Public Keys 24 | */ 25 | public final class X25519KeyFactory extends KeyFactory { 26 | /** 27 | * X25519 Key Factory constructor with default Security Provider 28 | * 29 | * @throws GeneralSecurityException Thrown on failures to construct required collaborators 30 | */ 31 | public X25519KeyFactory() throws GeneralSecurityException { 32 | this(new KeyAgreementFactory(), new KeyPairGeneratorFactory()); 33 | } 34 | 35 | /** 36 | * X25519 Key Factory constructor with specified Security Provider 37 | * 38 | * @param provider Security Provider for X25519 algorithm implementation resolution 39 | * @throws GeneralSecurityException Thrown on failures to construct required collaborators 40 | */ 41 | public X25519KeyFactory(final Provider provider) throws GeneralSecurityException { 42 | this(new KeyAgreementFactory(provider), new KeyPairGeneratorFactory(provider)); 43 | } 44 | 45 | private X25519KeyFactory(final KeyAgreementFactory keyAgreementFactory, final KeyPairGeneratorFactory keyPairGeneratorFactory) throws GeneralSecurityException { 46 | super(new X25519KeyFactorySpi(keyAgreementFactory, new StandardRecipientKeyFactory(keyPairGeneratorFactory)), null, RecipientIndicator.KEY_ALGORITHM.getIndicator()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jagged-x25519/src/main/java/com/exceptionfactory/jagged/x25519/X25519RecipientStanza.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import com.exceptionfactory.jagged.RecipientStanza; 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.Objects; 23 | 24 | /** 25 | * X25519 Recipient Stanza with standard type and single argument containing ephemeral shared public key 26 | */ 27 | class X25519RecipientStanza implements RecipientStanza { 28 | private final List arguments; 29 | 30 | private final byte[] body; 31 | 32 | X25519RecipientStanza(final String ephemeralShare, final byte[] body) { 33 | this.arguments = Collections.singletonList(Objects.requireNonNull(ephemeralShare, "Ephemeral Share required")); 34 | this.body = Objects.requireNonNull(body, "Stanza Body required"); 35 | } 36 | 37 | /** 38 | * Get Recipient Stanza Type returns X25519 39 | * 40 | * @return X25519 Type 41 | */ 42 | @Override 43 | public String getType() { 44 | return RecipientIndicator.STANZA_TYPE.getIndicator(); 45 | } 46 | 47 | /** 48 | * Get Recipient Stanza Arguments containing the ephemeral share encoded 49 | * 50 | * @return Recipient Stanza Arguments 51 | */ 52 | @Override 53 | public List getArguments() { 54 | return arguments; 55 | } 56 | 57 | /** 58 | * Get Recipient Stanza Body containing encrypted File Key with a length of 32 bytes 59 | * 60 | * @return Encrypted File Key body of 32 bytes 61 | */ 62 | @Override 63 | public byte[] getBody() { 64 | return body.clone(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /jagged-x25519/src/test/java/com/exceptionfactory/jagged/x25519/BasePointPublicKeyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | 22 | class BasePointPublicKeyTest { 23 | private static final int BASE_POINT = 9; 24 | 25 | private static final String FORMAT = "RAW"; 26 | 27 | @Test 28 | void testGetAlgorithm() { 29 | final BasePointPublicKey basePointPublicKey = new BasePointPublicKey(); 30 | 31 | assertEquals(RecipientIndicator.KEY_ALGORITHM.getIndicator(), basePointPublicKey.getAlgorithm()); 32 | } 33 | 34 | @Test 35 | void testGetFormat() { 36 | final BasePointPublicKey basePointPublicKey = new BasePointPublicKey(); 37 | 38 | assertEquals(FORMAT, basePointPublicKey.getFormat()); 39 | } 40 | 41 | @Test 42 | void testGetEncoded() { 43 | final BasePointPublicKey basePointPublicKey = new BasePointPublicKey(); 44 | 45 | final byte[] encoded = basePointPublicKey.getEncoded(); 46 | assertEquals(RecipientKeyType.X25519.getKeyLength(), encoded.length); 47 | assertEquals(BASE_POINT, encoded[0]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jagged-x25519/src/test/java/com/exceptionfactory/jagged/x25519/KeyPairGeneratorFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.security.KeyPairGenerator; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.Provider; 23 | import java.security.Security; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertNotNull; 26 | 27 | class KeyPairGeneratorFactoryTest { 28 | private static final String ALGORITHM_FILTER = String.format("KeyPairGenerator.%s", RecipientIndicator.KEY_ALGORITHM.getIndicator()); 29 | 30 | @Test 31 | void testGetKeyPairGenerator() throws NoSuchAlgorithmException { 32 | final KeyPairGeneratorFactory keyPairGeneratorFactory = new KeyPairGeneratorFactory(); 33 | final KeyPairGenerator keyPairGenerator = keyPairGeneratorFactory.getKeyPairGenerator(); 34 | 35 | assertNotNull(keyPairGenerator); 36 | } 37 | 38 | @Test 39 | void testGetKeyPairGeneratorWithProvider() throws NoSuchAlgorithmException { 40 | final Provider provider = getProvider(); 41 | final KeyPairGeneratorFactory keyPairGeneratorFactory = new KeyPairGeneratorFactory(provider); 42 | final KeyPairGenerator keyPairGenerator = keyPairGeneratorFactory.getKeyPairGenerator(); 43 | 44 | assertNotNull(keyPairGenerator); 45 | } 46 | 47 | private Provider getProvider() { 48 | final Provider[] providers = Security.getProviders(ALGORITHM_FILTER); 49 | return providers[0]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /jagged-x25519/src/test/java/com/exceptionfactory/jagged/x25519/X25519RecipientStanzaTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.util.Collections; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 23 | import static org.junit.jupiter.api.Assertions.assertEquals; 24 | 25 | class X25519RecipientStanzaTest { 26 | private static final byte[] BODY = new byte[]{}; 27 | 28 | private static final String EPHEMERAL_SHARE = String.class.getSimpleName(); 29 | 30 | @Test 31 | void testGetType() { 32 | final X25519RecipientStanza recipientStanza = new X25519RecipientStanza(EPHEMERAL_SHARE, BODY); 33 | 34 | assertEquals(RecipientIndicator.STANZA_TYPE.getIndicator(), recipientStanza.getType()); 35 | } 36 | 37 | @Test 38 | void testGetArguments() { 39 | final X25519RecipientStanza recipientStanza = new X25519RecipientStanza(EPHEMERAL_SHARE, BODY); 40 | 41 | assertEquals(Collections.singletonList(EPHEMERAL_SHARE), recipientStanza.getArguments()); 42 | } 43 | 44 | @Test 45 | void testGetBody() { 46 | final X25519RecipientStanza recipientStanza = new X25519RecipientStanza(EPHEMERAL_SHARE, BODY); 47 | 48 | assertArrayEquals(BODY, recipientStanza.getBody()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /jagged-x25519/src/test/java/com/exceptionfactory/jagged/x25519/X25519SharedSecretKeyProducerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Jagged Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.exceptionfactory.jagged.x25519; 17 | 18 | import com.exceptionfactory.jagged.framework.crypto.SharedSecretKey; 19 | import org.junit.jupiter.api.Test; 20 | 21 | import java.security.GeneralSecurityException; 22 | import java.security.KeyPair; 23 | import java.security.KeyPairGenerator; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 26 | 27 | class X25519SharedSecretKeyProducerTest { 28 | 29 | @Test 30 | void testGetSharedSecretKey() throws GeneralSecurityException { 31 | final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RecipientIndicator.KEY_ALGORITHM.getIndicator()); 32 | 33 | final KeyPair senderKeyPair = keyPairGenerator.generateKeyPair(); 34 | final KeyPair recipientKeyPair = keyPairGenerator.generateKeyPair(); 35 | 36 | final KeyAgreementFactory keyAgreementFactory = new KeyAgreementFactory(); 37 | 38 | final X25519SharedSecretKeyProducer senderProducer = new X25519SharedSecretKeyProducer(senderKeyPair.getPrivate(), keyAgreementFactory); 39 | final SharedSecretKey senderSharedSecretKey = senderProducer.getSharedSecretKey(recipientKeyPair.getPublic()); 40 | 41 | final X25519SharedSecretKeyProducer recipientProducer = new X25519SharedSecretKeyProducer(recipientKeyPair.getPrivate(), keyAgreementFactory); 42 | final SharedSecretKey recipientSharedSecretKey = recipientProducer.getSharedSecretKey(senderKeyPair.getPublic()); 43 | 44 | assertArrayEquals(senderSharedSecretKey.getEncoded(), recipientSharedSecretKey.getEncoded()); 45 | } 46 | } 47 | --------------------------------------------------------------------------------