-
Notifications
You must be signed in to change notification settings - Fork 603
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support cipher chacha20-poly1305@openssh.com (#682)
* Added cipher chacha20-poly1305@openssh.com * Small refactoring and remove mutable static buffer Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
- Loading branch information
1 parent
e283880
commit 16db036
Showing
11 changed files
with
343 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
src/main/java/com/hierynomus/sshj/transport/cipher/ChachaPolyCipher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Copyright (C)2009 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.hierynomus.sshj.transport.cipher; | ||
|
||
import java.security.GeneralSecurityException; | ||
import java.security.InvalidAlgorithmParameterException; | ||
import java.security.InvalidKeyException; | ||
|
||
import java.security.spec.AlgorithmParameterSpec; | ||
import java.util.Arrays; | ||
import javax.crypto.spec.IvParameterSpec; | ||
|
||
import net.schmizz.sshj.common.SSHRuntimeException; | ||
import net.schmizz.sshj.common.SecurityUtils; | ||
import net.schmizz.sshj.transport.cipher.BaseCipher; | ||
|
||
public class ChachaPolyCipher extends BaseCipher { | ||
|
||
private static final int CHACHA_KEY_SIZE = 32; | ||
private static final int AAD_LENGTH = 4; | ||
private static final int POLY_TAG_LENGTH = 16; | ||
|
||
private static final String CIPHER_CHACHA = "CHACHA"; | ||
private static final String MAC_POLY1305 = "POLY1305"; | ||
|
||
private static final byte[] POLY_KEY_INPUT = new byte[32]; | ||
|
||
private final int authSize; | ||
|
||
private byte[] encryptedAad; | ||
|
||
protected Mode mode; | ||
protected javax.crypto.Cipher aadCipher; | ||
protected javax.crypto.Mac mac; | ||
protected java.security.Key cipherKey; | ||
protected java.security.Key aadCipherKey; | ||
|
||
public ChachaPolyCipher(int authSize, int bsize, String algorithm) { | ||
super(0, bsize, algorithm, CIPHER_CHACHA); | ||
this.authSize = authSize; | ||
} | ||
|
||
@Override | ||
public int getAuthenticationTagSize() { | ||
return authSize; | ||
} | ||
|
||
@Override | ||
public void setSequenceNumber(long seq) { | ||
byte[] seqAsBytes = longToBytes(seq); | ||
AlgorithmParameterSpec ivSpec = new IvParameterSpec(seqAsBytes); | ||
|
||
try { | ||
cipher.init(getMode(mode), cipherKey, ivSpec); | ||
aadCipher.init(getMode(mode), aadCipherKey, ivSpec); | ||
} catch (GeneralSecurityException e) { | ||
throw new SSHRuntimeException(e); | ||
} | ||
|
||
byte[] polyKeyBytes = cipher.update(POLY_KEY_INPUT); | ||
cipher.update(POLY_KEY_INPUT); // this update is required to set the block counter of ChaCha to 1 | ||
try { | ||
mac.init(getKeySpec(polyKeyBytes)); | ||
} catch (GeneralSecurityException e) { | ||
throw new SSHRuntimeException(e); | ||
} | ||
|
||
encryptedAad = null; | ||
} | ||
|
||
@Override | ||
protected void initCipher(javax.crypto.Cipher cipher, Mode mode, byte[] key, byte[] iv) | ||
throws InvalidKeyException, InvalidAlgorithmParameterException { | ||
this.mode = mode; | ||
|
||
cipherKey = getKeySpec(Arrays.copyOfRange(key, 0, CHACHA_KEY_SIZE)); | ||
aadCipherKey = getKeySpec(Arrays.copyOfRange(key, CHACHA_KEY_SIZE, 2 * CHACHA_KEY_SIZE)); | ||
|
||
try { | ||
aadCipher = SecurityUtils.getCipher(CIPHER_CHACHA); | ||
mac = SecurityUtils.getMAC(MAC_POLY1305); | ||
} catch (GeneralSecurityException e) { | ||
cipher = null; | ||
aadCipher = null; | ||
mac = null; | ||
throw new SSHRuntimeException(e); | ||
} | ||
|
||
setSequenceNumber(0); | ||
} | ||
|
||
@Override | ||
public void updateAAD(byte[] data, int offset, int length) { | ||
if (offset != 0 || length != AAD_LENGTH) { | ||
throw new IllegalArgumentException( | ||
String.format("updateAAD called with offset %d and length %d", offset, length)); | ||
} | ||
|
||
if (mode == Mode.Decrypt) { | ||
encryptedAad = Arrays.copyOfRange(data, 0, AAD_LENGTH); | ||
} | ||
|
||
try { | ||
aadCipher.update(data, 0, AAD_LENGTH, data, 0); | ||
} catch (GeneralSecurityException e) { | ||
throw new SSHRuntimeException("Error updating data through cipher", e); | ||
} | ||
} | ||
|
||
@Override | ||
public void updateAAD(byte[] data) { | ||
updateAAD(data, 0, AAD_LENGTH); | ||
} | ||
|
||
@Override | ||
public void update(byte[] input, int inputOffset, int inputLen) { | ||
if (inputOffset != AAD_LENGTH) { | ||
throw new IllegalArgumentException("updateAAD called with inputOffset " + inputOffset); | ||
} | ||
|
||
final int macInputLength = AAD_LENGTH + inputLen; | ||
|
||
if (mode == Mode.Decrypt) { | ||
byte[] macInput = new byte[macInputLength]; | ||
System.arraycopy(encryptedAad, 0, macInput, 0, AAD_LENGTH); | ||
System.arraycopy(input, AAD_LENGTH, macInput, AAD_LENGTH, inputLen); | ||
|
||
byte[] expectedPolyTag = mac.doFinal(macInput); | ||
byte[] actualPolyTag = Arrays.copyOfRange(input, macInputLength, macInputLength + POLY_TAG_LENGTH); | ||
if (!Arrays.equals(actualPolyTag, expectedPolyTag)) { | ||
throw new SSHRuntimeException("MAC Error"); | ||
} | ||
} | ||
|
||
try { | ||
cipher.update(input, AAD_LENGTH, inputLen, input, AAD_LENGTH); | ||
} catch (GeneralSecurityException e) { | ||
throw new SSHRuntimeException("Error updating data through cipher", e); | ||
} | ||
|
||
if (mode == Mode.Encrypt) { | ||
byte[] macInput = Arrays.copyOf(input, macInputLength); | ||
byte[] polyTag = mac.doFinal(macInput); | ||
System.arraycopy(polyTag, 0, input, macInputLength, POLY_TAG_LENGTH); | ||
} | ||
} | ||
|
||
private byte[] longToBytes(long lng) { | ||
return new byte[] { (byte) (lng >> 56), (byte) (lng >> 48), (byte) (lng >> 40), (byte) (lng >> 32), | ||
(byte) (lng >> 24), (byte) (lng >> 16), (byte) (lng >> 8), (byte) lng }; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/main/java/com/hierynomus/sshj/transport/cipher/ChachaPolyCiphers.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright (C)2009 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.hierynomus.sshj.transport.cipher; | ||
|
||
import net.schmizz.sshj.transport.cipher.Cipher; | ||
|
||
public class ChachaPolyCiphers { | ||
|
||
public static Factory CHACHA_POLY_OPENSSH() { | ||
return new Factory(16, 512, "chacha20-poly1305@openssh.com", "ChaCha20"); | ||
} | ||
|
||
public static class Factory | ||
implements net.schmizz.sshj.common.Factory.Named<Cipher> { | ||
|
||
private final int authSize; | ||
private final int keySize; | ||
private final String name; | ||
private final String cipher; | ||
|
||
public Factory(int authSize, int keySize, String name, String cipher) { | ||
this.authSize = authSize; | ||
this.keySize = keySize; | ||
this.name = name; | ||
this.cipher = cipher; | ||
} | ||
|
||
@Override | ||
public Cipher create() { | ||
return new ChachaPolyCipher(authSize, keySize / 8, cipher); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return getName(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.