2019-05-23 12:11:37 +02:00
/ * *
* @ author j433866 [ j433866 @ gmail . com ]
* @ copyright Crown Copyright 2019
* @ license Apache - 2.0
* /
2019-11-06 14:01:52 +01:00
import Operation from "../Operation.mjs" ;
import OperationError from "../errors/OperationError.mjs" ;
import Utils from "../Utils.mjs" ;
import { fromBase64 } from "../lib/Base64.mjs" ;
import { fromHex , toHexFast } from "../lib/Hex.mjs" ;
2019-05-23 12:11:37 +02:00
/ * *
* Parse SSH Host Key operation
* /
class ParseSSHHostKey extends Operation {
/ * *
* ParseSSHHostKey constructor
* /
constructor ( ) {
super ( ) ;
this . name = "Parse SSH Host Key" ;
this . module = "Default" ;
2022-09-18 12:25:40 +02:00
this . description = "Parses a SSH host key and extracts fields from it.<br>The key type can be:<ul><li>ssh-rsa</li><li>ssh-dss</li><li>ecdsa-sha2</li><li>ssh-ed25519</li></ul>The key format can be either Hex or Base64." ;
2019-05-23 12:11:37 +02:00
this . infoURL = "https://wikipedia.org/wiki/Secure_Shell" ;
this . inputType = "string" ;
this . outputType = "string" ;
this . args = [
{
name : "Input Format" ,
type : "option" ,
value : [
"Auto" ,
"Base64" ,
"Hex"
]
}
] ;
2020-03-24 12:06:37 +01:00
this . checks = [
{
pattern : "^\\s*([A-F\\d]{2}[,;:]){15,}[A-F\\d]{2}\\s*$" ,
flags : "i" ,
args : [ "Hex" ]
2020-02-25 12:27:03 +01:00
}
2020-03-24 12:06:37 +01:00
] ;
2019-05-23 12:11:37 +02:00
}
/ * *
* @ param { string } input
* @ param { Object [ ] } args
* @ returns { string }
* /
run ( input , args ) {
const [ inputFormat ] = args ,
inputKey = this . convertKeyToBinary ( input . trim ( ) , inputFormat ) ,
fields = this . parseKey ( inputKey ) ,
keyType = Utils . byteArrayToChars ( fromHex ( fields [ 0 ] ) , "" ) ;
let output = ` Key type: ${ keyType } ` ;
if ( keyType === "ssh-rsa" ) {
output += ` \n Exponent: 0x ${ fields [ 1 ] } ` ;
output += ` \n Modulus: 0x ${ fields [ 2 ] } ` ;
} else if ( keyType === "ssh-dss" ) {
output += ` \n p: 0x ${ fields [ 1 ] } ` ;
output += ` \n q: 0x ${ fields [ 2 ] } ` ;
output += ` \n g: 0x ${ fields [ 3 ] } ` ;
output += ` \n y: 0x ${ fields [ 4 ] } ` ;
} else if ( keyType . startsWith ( "ecdsa-sha2" ) ) {
2019-07-08 17:58:03 +02:00
output += ` \n Curve: ${ Utils . byteArrayToChars ( fromHex ( fields [ 1 ] ) ) } ` ;
2019-05-23 12:11:37 +02:00
output += ` \n Point: 0x ${ fields . slice ( 2 ) } ` ;
2022-09-18 12:25:40 +02:00
} else if ( keyType === "ssh-ed25519" ) {
output += ` \n x: 0x ${ fields [ 1 ] } ` ;
2019-05-23 12:11:37 +02:00
} else {
output += "\nUnsupported key type." ;
output += ` \n Parameters: ${ fields . slice ( 1 ) } ` ;
}
return output ;
}
/ * *
* Converts the key to binary format from either hex or base64
*
* @ param { string } inputKey
* @ param { string } inputFormat
* @ returns { byteArray }
* /
convertKeyToBinary ( inputKey , inputFormat ) {
2021-02-10 14:13:19 +01:00
const keyPattern = new RegExp ( /^(?:ssh|ecdsa-sha2)\S+\s+(\S*)/ ) ,
2019-07-08 16:58:56 +02:00
keyMatch = inputKey . match ( keyPattern ) ;
if ( keyMatch ) {
inputKey = keyMatch [ 1 ] ;
}
2019-05-23 12:11:37 +02:00
if ( inputFormat === "Auto" ) {
inputFormat = this . detectKeyFormat ( inputKey ) ;
}
if ( inputFormat === "Hex" ) {
return fromHex ( inputKey ) ;
} else if ( inputFormat === "Base64" ) {
return fromBase64 ( inputKey , null , "byteArray" ) ;
} else {
throw new OperationError ( "Invalid input format." ) ;
}
}
/ * *
* Detects if the key is base64 or hex encoded
*
* @ param { string } inputKey
* @ returns { string }
* /
detectKeyFormat ( inputKey ) {
const hexPattern = new RegExp ( /^(?:[\dA-Fa-f]{2}[ ,;:]?)+$/ ) ;
const b64Pattern = new RegExp ( /^\s*(?:[A-Za-z\d+/]{4})+(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?\s*$/ ) ;
if ( hexPattern . test ( inputKey ) ) {
return "Hex" ;
} else if ( b64Pattern . test ( inputKey ) ) {
return "Base64" ;
} else {
throw new OperationError ( "Unable to detect input key format." ) ;
}
}
/ * *
* Parses fields from the key
*
* @ param { byteArray } key
* /
parseKey ( key ) {
const fields = [ ] ;
while ( key . length > 0 ) {
const lengthField = key . slice ( 0 , 4 ) ;
let decodedLength = 0 ;
for ( let i = 0 ; i < lengthField . length ; i ++ ) {
decodedLength += lengthField [ i ] ;
decodedLength = decodedLength << 8 ;
}
decodedLength = decodedLength >> 8 ;
// Break if length wasn't decoded correctly
if ( decodedLength <= 0 ) break ;
fields . push ( toHexFast ( key . slice ( 4 , 4 + decodedLength ) ) ) ;
key = key . slice ( 4 + decodedLength ) ;
}
return fields ;
}
}
export default ParseSSHHostKey ;