Skip to content

Commit e55e3ec

Browse files
committed
check for unknown digest
1 parent a0ffbc9 commit e55e3ec

3 files changed

Lines changed: 55 additions & 15 deletions

File tree

src/lib/decrypt_transform.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ export class DecryptTransform extends Transform {
55
private decipher?: crypto.Decipher
66
private algo: string
77
private cipherKey: crypto.KeyObject
8-
private cipherIvLength: number
8+
private ivLength: number
99
private inputEncoding?: BufferEncoding
1010

1111
constructor(
1212
algo: string,
1313
cipherKey: crypto.KeyObject,
14-
cipherIvLength: number,
14+
ivLength: number,
1515
inputEncoding?: BufferEncoding,
1616
opts?: TransformOptions,
1717
) {
1818
super(opts)
1919
this.algo = algo
2020
this.cipherKey = cipherKey
21-
this.cipherIvLength = cipherIvLength
21+
this.ivLength = ivLength
2222
this.inputEncoding = inputEncoding
2323
}
2424

@@ -30,9 +30,9 @@ export class DecryptTransform extends Transform {
3030
if (!this.decipher) {
3131
// Unpackage the combined iv + encrypted message.
3232
// Since we are using a fixed size IV, we can hard code the slice length.
33-
const iv = data.subarray(0, this.cipherIvLength)
33+
const iv = data.subarray(0, this.ivLength)
3434
this.decipher = crypto.createDecipheriv(this.algo, this.cipherKey, iv)
35-
data = data.subarray(this.cipherIvLength)
35+
data = data.subarray(this.ivLength)
3636
}
3737
this.push(this.decipher.update(data))
3838
callback()

src/lib/ssh_agent_client.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ export class SSHAgentClient {
7474

7575
/**
7676
* @param options - Optional configuration.
77-
* @throws {Error} if SSH_AUTH_SOCK is not set.
77+
* @throws {Error} if SSH_AUTH_SOCK is not set or socket not found.
7878
*/
7979
constructor(options: SSHAgentClientOptions = {}) {
8080
/** Socket operation timeout in milliseconds (default: 10000) */
8181
this.timeout = options.timeout ?? 10000
8282

83-
/** Encryption and algo key length must match */
83+
/** Digest and cipher key length must match */
8484
this.cipherAlgo = options.cipherAlgo ?? 'aes-256-cbc'
8585
this.digestAlgo = options.digestAlgo ?? 'sha256'
8686

@@ -180,6 +180,12 @@ export class SSHAgentClient {
180180
return this.request(buildRequest, parseResponse, Protocol.SSH2_AGENT_SIGN_RESPONSE)
181181
}
182182

183+
/**
184+
* Encrypt data with given `SSHKey` and `seed` string, using SSH signature as the encryption key.
185+
*
186+
* Resolves with a string containing the IV and encrypted data, encoded in `outputEncoding` (default: "hex").
187+
* The IV is needed for decryption and is included in the output as a prefix to the encrypted data.
188+
*/
183189
async encrypt(
184190
key: SSHKey,
185191
seed: string,
@@ -192,6 +198,13 @@ export class SSHAgentClient {
192198
)
193199
}
194200

201+
/**
202+
* Get a Transform stream that encrypts data with given `SSHKey` and `seed` string, using SSH signature as
203+
* the encryption key.
204+
*
205+
* The Transform stream outputs plain binary or a string encoded in `outputEncoding`.
206+
* The IV is needed for decryption and is included in the output as a prefix to the encrypted data.
207+
*/
195208
async getEncryptTransform(
196209
key: SSHKey,
197210
seed: string,
@@ -206,6 +219,12 @@ export class SSHAgentClient {
206219
})
207220
}
208221

222+
/**
223+
* Decrypt data with given `SSHKey` and `seed` string, using SSH signature as the decryption key.
224+
*
225+
* Resolves with a Buffer containing the decrypted data.
226+
* The IV is needed for decryption and should be included in the input as a prefix to the encrypted data.
227+
*/
209228
async decrypt(
210229
key: SSHKey,
211230
seed: string,
@@ -223,6 +242,14 @@ export class SSHAgentClient {
223242
})
224243
}
225244

245+
/**
246+
* Get a Transform stream that decrypts data with given `SSHKey` and `seed` string, using SSH signature as
247+
* the decryption key.
248+
*
249+
* The Transform stream expects encrypted data (as plain binary or a string encoded in `inputEncoding`) and
250+
* outputs a Buffer containing the decrypted data.
251+
* The IV is needed for decryption and should be included in the input as a prefix to the encrypted data.
252+
*/
226253
async getDecryptTransform(
227254
key: SSHKey,
228255
seed: string,
@@ -252,14 +279,17 @@ export class SSHAgentClient {
252279
return this.sign(key, Buffer.from(seed, 'utf8')).then(signature => {
253280
const cipherInfo = crypto.getCipherInfo(this.cipherAlgo)
254281
if (!cipherInfo?.ivLength) {
255-
throw new Error('Wrong cipher algo')
282+
throw new Error('Unknown symmetric cipher algo')
283+
}
284+
if (!crypto.getHashes().includes(this.digestAlgo)) {
285+
throw new Error('Unknown digest algo')
256286
}
257-
const hash = crypto.createHash(this.digestAlgo).update(signature.raw).digest()
258-
if (hash.length < cipherInfo.keyLength) {
259-
throw new Error("Digest algo doesn't match cipher key length")
287+
const digest = crypto.createHash(this.digestAlgo).update(signature.raw).digest()
288+
if (digest.length < cipherInfo.keyLength) {
289+
throw new Error("Digest length doesn't match cipher key length")
260290
}
261291
return {
262-
cipherKey: crypto.createSecretKey(hash.subarray(0, cipherInfo.keyLength)),
292+
cipherKey: crypto.createSecretKey(digest.subarray(0, cipherInfo.keyLength)),
263293
ivLength: cipherInfo.ivLength,
264294
}
265295
})

test/ssh_agent_client.spec.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,15 @@ describe('SSHAgentClient tests', () => {
102102
.expect(agent.decrypt(identity, SEED, data))
103103
.to.be.rejectedWith(Error, 'error:1C80006B:Provider routines::wrong final block length')
104104
})
105-
it('should throw if unknown cipher', async () => {
105+
it('should throw if cipher algorithm is unknown', async () => {
106106
const agent = new SSHAgentClient({ cipherAlgo: 'xxx' })
107107
const identity = await agent.getIdentity('key_rsa')
108108
if (!identity) {
109109
throw new Error()
110110
}
111111
return chai
112112
.expect(agent.encrypt(identity, SEED, DECODED_STRING_BUFFER))
113-
.to.be.rejectedWith(Error, 'Wrong cipher algo')
113+
.to.be.rejectedWith(Error, 'Unknown symmetric cipher algo')
114114
})
115115
it('should throw if digest length is less than cipher key length', async () => {
116116
const agent = new SSHAgentClient({ cipherAlgo: 'aes-192-cbc', digestAlgo: 'sha1' })
@@ -120,7 +120,17 @@ describe('SSHAgentClient tests', () => {
120120
}
121121
return chai
122122
.expect(agent.encrypt(identity, SEED, DECODED_STRING_BUFFER))
123-
.to.be.rejectedWith(Error, "Digest algo doesn't match cipher key length")
123+
.to.be.rejectedWith(Error, "Digest length doesn't match cipher key length")
124+
})
125+
it('should throw if hash algorithm is unknown', async () => {
126+
const agent = new SSHAgentClient({ digestAlgo: 'xxx' })
127+
const identity = await agent.getIdentity('key_rsa')
128+
if (!identity) {
129+
throw new Error()
130+
}
131+
return chai
132+
.expect(agent.encrypt(identity, SEED, DECODED_STRING_BUFFER))
133+
.to.be.rejectedWith(Error, 'Unknown digest algo')
124134
})
125135
})
126136

0 commit comments

Comments
 (0)