include "socket.s7i";
include "asn1.s7i";
include "x509cert.s7i";
include "hmac.s7i";
include "elliptic.s7i";
include "cipher.s7i";
include "arc4.s7i";
include "des.s7i";
include "tdes.s7i";
include "aes.s7i";
include "aes_gcm.s7i";
const type: cipherSuite is integer;
const cipherSuite: TLS_NULL_WITH_NULL_NULL is 16#0000;
const cipherSuite: TLS_RSA_WITH_RC4_128_MD5 is 16#0004;
const cipherSuite: TLS_RSA_WITH_RC4_128_SHA is 16#0005;
const cipherSuite: TLS_RSA_WITH_DES_CBC_SHA is 16#0009;
const cipherSuite: TLS_RSA_WITH_3DES_EDE_CBC_SHA is 16#000a;
const cipherSuite: TLS_RSA_WITH_AES_128_CBC_SHA is 16#002f;
const cipherSuite: TLS_RSA_WITH_AES_256_CBC_SHA is 16#0035;
const cipherSuite: TLS_RSA_WITH_AES_128_CBC_SHA256 is 16#003c;
const cipherSuite: TLS_RSA_WITH_AES_256_CBC_SHA256 is 16#003d;
const cipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA is 16#c009;
const cipherSuite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is 16#c00a;
const cipherSuite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA is 16#c013;
const cipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 is 16#c02b;
const cipherSuite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 is 16#c02c;
const cipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 is 16#c02f;
const cipherSuite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 is 16#c030;
const array cipherSuite: supportedCiphers is [] (
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_RC4_128_MD5,
TLS_RSA_WITH_DES_CBC_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_256_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
);
const string: SSL_3_0 is "\3;\0;";
const string: TLS_1_0 is "\3;\1;";
const string: TLS_1_1 is "\3;\2;";
const string: TLS_1_2 is "\3;\3;";
const string: TLS_1_3 is "\3;\4;";
const type: keyExchangeAlgorithm is new enum
RSA, EC_DIFFIE_HELLMAN
end enum;
const type: tlsParameters is new struct
var boolean: isClient is TRUE;
var string: session_id is "";
var string: hostName is "";
var cipherSuite: cipher_suite is TLS_NULL_WITH_NULL_NULL;
var keyExchangeAlgorithm: key_exchange_algorithm is RSA;
var cipherAlgorithm: bulk_cipher_algorithm is NO_CIPHER;
var boolean: block_cipher is FALSE;
var integer: key_size is 0;
var integer: key_material_length is 0;
var integer: block_size is 0;
var integer: iv_size is 0;
var boolean: is_exportable is FALSE;
var digestAlgorithm: mac_algorithm is NO_DIGEST;
var integer: hash_size is 0;
var integer: compression_algorithm is 0;
var string: tls_version is TLS_1_2;
var string: master_secret is "";
var string: client_random is "";
var string: server_random is "";
var certAndKey: serverCertificates is certAndKey.value;
var certAndKey: clientCertificates is certAndKey.value;
var rsaKey: publicRsaCertificateKey is rsaKey.value;
var rsaKey: privateRsaCertificateKey is rsaKey.value;
var ecPoint: publicEccCertificateKey is ecPoint.value;
var bigInteger: privateEccCertificateKey is 0_;
var ellipticCurve: curve is ellipticCurve.value;
var integer: signatureScheme is 0;
var eccKeyPair: ownEccKeyPair is eccKeyPair.value;
var ecPoint: publicEccKeyOfServer is ecPoint.value;
var string: readMacSecret is "";
var string: writeMacSecret is "";
var cipherState: readCipherState is cipherState.value;
var cipherState: writeCipherState is cipherState.value;
var integer: readSequenceNumber is 0;
var integer: writeSequenceNumber is 0;
var boolean: writeEncryptedRecords is FALSE;
var string: handshake_messages is "";
end struct;
const char: NO_MESSAGE is EOF;
const char: CHANGE_CIPHER_SPEC is '\20;';
const char: ALERT is '\21;';
const char: HANDSHAKE is '\22;';
const char: APPLICATION_DATA is '\23;';
const char: CLIENT_HELLO is '\1;';
const char: SERVER_HELLO is '\2;';
const char: SESSION_TICKET is '\4;';
const char: CERTIFICATE is '\11;';
const char: SERVER_KEY_EXCHANGE is '\12;';
const char: CERTIFICATE_REQUEST is '\13;';
const char: SERVER_HELLO_DONE is '\14;';
const char: CERTIFICATE_VERIFY is '\15;';
const char: CLIENT_KEY_EXCHANGE is '\16;';
const char: FINISHED is '\20;';
const char: CLOSE_NOTIFY is '\0;';
const char: UNEXPECTED_MESSAGE is '\10;';
const char: BAD_RECORD_MAC is '\20;';
const char: DECRYPTION_FAILED is '\21;';
const char: HANDSHAKE_FAILURE is '\40;';
const char: BAD_CERTIFICATE is '\42;';
const char: ILLEGAL_PARAMETER is '\47;';
const char: PROCOCOL_VERSION is '\70;';
const char: NO_RENEGOTIATION is '\100;';
const integer: SERVER_NAME is 0;
const integer: MAX_FRAGMENT_LENGTH is 1;
const integer: CLIENT_CERTIFICATE_URL is 2;
const integer: TRUSTED_CA_KEYS is 3;
const integer: TRUNCATED_HMAC is 4;
const integer: STATUS_REQUEST is 5;
const integer: USER_MAPPING is 6;
const integer: CLIENT_AUTHZ is 7;
const integer: SERVER_AUTHZ is 8;
const integer: CERT_TYPE is 9;
const integer: ELLIPTIC_CURVES is 10;
const integer: EC_POINT_FORMATS is 11;
const integer: SRP is 12;
const integer: SIGNATURE_ALGORITHMS is 13;
const integer: USE_SRTP is 14;
const integer: HEARTBEAT is 15;
const integer: APPLICATION_LAYER_PROTOCOL_NEGOTIATION is 16;
const integer: STATUS_REQUEST_V2 is 17;
const integer: SIGNED_CERTIFICATE_TIMESTAMP is 18;
const integer: CLIENT_CERTIFICATE_TYPE is 19;
const integer: SERVER_CERTIFICATE_TYPE is 20;
const integer: PADDING is 21;
const integer: ENCRYPT_THEN_MAC is 22;
const integer: EXTENDED_MASTER_SECRET is 23;
const integer: TOKEN_BINDING is 24;
const integer: CACHED_INFO is 25;
const integer: TLS_LTS is 26;
const integer: COMPRESS_CERTIFICATE is 27;
const integer: RECORD_SIZE_LIMIT is 28;
const integer: PWD_PROTECT is 29;
const integer: PWD_CLEAR is 30;
const integer: PASSWORD_SALT is 31;
const integer: TICKET_PINNING is 32;
const integer: TLS_CERT_WITH_EXTERN_PSK is 33;
const integer: DELEGATED_CREDENTIAL is 34;
const integer: SESSION_TICKET_TLS is 35;
const integer: PRE_SHARED_KEY is 41;
const integer: EARLY_DATA is 42;
const integer: SUPPORTED_VERSIONS is 43;
const integer: COOKIE is 44;
const integer: PSK_KEY_EXCHANGE_MODES is 45;
const integer: CERTIFICATE_AUTHORITIES is 47;
const integer: OID_FILTERS is 48;
const integer: POST_HANDSHAKE_AUTH is 49;
const integer: SIGNATURE_ALGORITHMS_CERT is 50;
const integer: KEY_SHARE is 51;
const integer: TRANSPARENCY_INFO is 52;
const integer: CONNECTION_ID_DEPRECATED is 53;
const integer: CONNECTION_ID is 54;
const integer: EXTERNAL_ID_HASH is 55;
const integer: EXTERNAL_SESSION_ID is 56;
const integer: QUIC_TRANSPORT_PARAMETERS is 57;
const integer: TICKET_REQUEST is 58;
const integer: DNSSEC_CHAIN is 59;
const integer: NEXT_PROTOCOL_NEGOTIATION is 13172;
const integer: ENCRYPTED_CLIENT_HELLO is 65037;
const integer: RENEGOTIATION_INFO is 65281;
const integer: SECP192K1 is 18;
const integer: SECP192R1 is 19;
const integer: SECP224K1 is 20;
const integer: SECP224R1 is 21;
const integer: SECP256K1 is 22;
const integer: SECP256R1 is 23;
const integer: SECP384R1 is 24;
const integer: SECP521R1 is 25;
const array ellipticCurve: curveByNumber is [SECP192K1] (
secp192k1, secp192r1, secp224k1, secp224r1, secp256k1,
secp256r1, secp384r1, secp521r1);
const char: NAMED_CURVE is '\3;';
const array digestAlgorithm: signatureHashByNumber is [1] (
MD5, SHA1, SHA224, SHA256, SHA384, SHA512);
const integer: RSA_PKCS1_MD5_SHA1 is 16#0100;
const integer: RSA_PKCS1_SHA1 is 16#0201;
const integer: RSA_PKCS1_SHA224 is 16#0301;
const integer: RSA_PKCS1_SHA256 is 16#0401;
const integer: RSA_PKCS1_SHA384 is 16#0501;
const integer: RSA_PKCS1_SHA512 is 16#0601;
const integer: ECDSA_SHA1 is 16#0203;
const integer: ECDSA_SECP256R1_SHA256 is 16#0403;
const array integer: signatureSchemes is [] (
ECDSA_SECP256R1_SHA256, RSA_PKCS1_SHA256, RSA_PKCS1_SHA1, ECDSA_SHA1, RSA_PKCS1_MD5_SHA1);
const array integer: serverSignatureSchemesRsa is [] (
RSA_PKCS1_SHA512, RSA_PKCS1_SHA384, RSA_PKCS1_SHA256, RSA_PKCS1_SHA1,
RSA_PKCS1_MD5_SHA1);
const array integer: serverSignatureSchemesEcdsa is [] (
ECDSA_SECP256R1_SHA256, ECDSA_SHA1);
const type: tlsParseState is new struct
var char: contentType is NO_MESSAGE;
var integer: length is 0;
var string: message is "";
var integer: pos is 1;
var char: alert is CLOSE_NOTIFY;
end struct;
const type: tlsFile is sub null_file struct
var file: sock is STD_NULL;
var tlsParseState: parseState is tlsParseState.value;
var tlsParameters: parameters is tlsParameters.value;
var string: readBuffer is "";
end struct;
const type: clientSession is new struct
var string: session_id is "";
var cipherAlgorithm: bulk_cipher_algorithm is NO_CIPHER;
var string: master_secret is "";
var time: last_use is time.value;
end struct;
const duration: clientCacheValid is 1 . MINUTES;
const type: clientSessionCacheType is hash [socketAddress] clientSession;
var clientSessionCacheType: clientSessionCache is clientSessionCacheType.value;
const string: MD5_PAD1 is "\16#36;" mult 48;
const string: MD5_PAD2 is "\16#5c;" mult 48;
const string: SHA_PAD1 is "\16#36;" mult 40;
const string: SHA_PAD2 is "\16#5c;" mult 40;
const func integer: getEllipticCurveNumber (in ellipticCurve: curve) is func
result
var integer: curveNumber is 0;
begin
if curve.name = "secp192k1" then
curveNumber := SECP192K1;
elsif curve.name = "secp192r1" then
curveNumber := SECP192R1;
elsif curve.name = "secp224k1" then
curveNumber := SECP224K1;
elsif curve.name = "secp224r1" then
curveNumber := SECP224R1;
elsif curve.name = "secp256k1" then
curveNumber := SECP256K1;
elsif curve.name = "secp256r1" then
curveNumber := SECP256R1;
elsif curve.name = "secp384r1" then
curveNumber := SECP384R1;
elsif curve.name = "secp512r1" then
curveNumber := SECP521R1;
end if;
end func;
const proc: storeCipherSuite (inout tlsParameters: parameters) is func
begin
if parameters.cipher_suite = TLS_NULL_WITH_NULL_NULL then
parameters.bulk_cipher_algorithm := NO_CIPHER;
parameters.key_material_length := 0;
parameters.mac_algorithm := NO_DIGEST;
elsif parameters.cipher_suite = TLS_RSA_WITH_RC4_128_MD5 then
parameters.bulk_cipher_algorithm := RC4;
parameters.key_material_length := 16;
parameters.mac_algorithm := MD5;
elsif parameters.cipher_suite = TLS_RSA_WITH_RC4_128_SHA then
parameters.bulk_cipher_algorithm := RC4;
parameters.key_material_length := 16;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_RSA_WITH_3DES_EDE_CBC_SHA then
parameters.bulk_cipher_algorithm := TDES;
parameters.key_material_length := 24;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_RSA_WITH_DES_CBC_SHA then
parameters.bulk_cipher_algorithm := DES;
parameters.key_material_length := 8;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_RSA_WITH_AES_128_CBC_SHA then
parameters.bulk_cipher_algorithm := AES;
parameters.key_material_length := 16;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_RSA_WITH_AES_256_CBC_SHA then
parameters.bulk_cipher_algorithm := AES;
parameters.key_material_length := 32;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_RSA_WITH_AES_128_CBC_SHA256 then
parameters.bulk_cipher_algorithm := AES;
parameters.key_material_length := 16;
parameters.mac_algorithm := SHA256;
elsif parameters.cipher_suite = TLS_RSA_WITH_AES_256_CBC_SHA256 then
parameters.bulk_cipher_algorithm := AES;
parameters.key_material_length := 32;
parameters.mac_algorithm := SHA256;
elsif parameters.cipher_suite = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA then
parameters.key_exchange_algorithm := EC_DIFFIE_HELLMAN;
parameters.bulk_cipher_algorithm := AES;
parameters.key_material_length := 16;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA then
parameters.key_exchange_algorithm := EC_DIFFIE_HELLMAN;
parameters.bulk_cipher_algorithm := AES;
parameters.key_material_length := 16;
parameters.mac_algorithm := SHA1;
elsif parameters.cipher_suite = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 then
parameters.key_exchange_algorithm := EC_DIFFIE_HELLMAN;
parameters.bulk_cipher_algorithm := AES_GCM;
parameters.key_material_length := 16;
parameters.iv_size := 4;
parameters.mac_algorithm := NO_DIGEST;
elsif parameters.cipher_suite = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 then
parameters.key_exchange_algorithm := EC_DIFFIE_HELLMAN;
parameters.bulk_cipher_algorithm := AES_GCM;
parameters.key_material_length := 16;
parameters.iv_size := 4;
parameters.mac_algorithm := NO_DIGEST;
else
writeln("Unsupported cipher_suite: " <& ord(parameters.cipher_suite) radix 16 lpad0 4);
end if;
parameters.block_size := blockSize(parameters.bulk_cipher_algorithm);
if parameters.iv_size = 0 then
parameters.iv_size := parameters.block_size;
end if;
parameters.hash_size := digestSize(parameters.mac_algorithm);
end func;
const proc: computeMasterSecret (inout tlsParameters: parameters,
in string: preMasterSecret) is func
begin
if parameters.tls_version = SSL_3_0 then
parameters.master_secret := keyBlockFunction(preMasterSecret,
parameters.client_random & parameters.server_random, 48);
elsif parameters.tls_version >= TLS_1_2 then
parameters.master_secret := p_hash(SHA256, preMasterSecret,
"master secret" & parameters.client_random & parameters.server_random, 48);
else
parameters.master_secret := pseudoRandomFunction(preMasterSecret,
"master secret", parameters.client_random & parameters.server_random, 48);
end if;
end func;
const proc: storeKeys (inout tlsParameters: parameters) is func
local
var string: key_block is "";
var integer: key_block_pos is 1;
var string: client_write_MAC_secret is "";
var string: server_write_MAC_secret is "";
var string: client_write_key is "";
var string: server_write_key is "";
var string: client_initialization_vector is "";
var string: server_initialization_vector is "";
begin
if parameters.tls_version = SSL_3_0 then
key_block := keyBlockFunction(parameters.master_secret,
parameters.server_random & parameters.client_random,
2 * parameters.hash_size + 2 * parameters.key_material_length + 2 * parameters.iv_size);
elsif parameters.tls_version >= TLS_1_2 then
key_block := p_hash(SHA256, parameters.master_secret,
"key expansion" & parameters.server_random & parameters.client_random,
2 * parameters.hash_size + 2 * parameters.key_material_length + 2 * parameters.iv_size);
else
key_block := pseudoRandomFunction(parameters.master_secret,
"key expansion", parameters.server_random & parameters.client_random,
2 * parameters.hash_size + 2 * parameters.key_material_length + 2 * parameters.iv_size);
end if;
client_write_MAC_secret := key_block[key_block_pos fixLen parameters.hash_size];
key_block_pos +:= parameters.hash_size;
server_write_MAC_secret := key_block[key_block_pos fixLen parameters.hash_size];
key_block_pos +:= parameters.hash_size;
client_write_key := key_block[key_block_pos fixLen parameters.key_material_length];
key_block_pos +:= parameters.key_material_length;
server_write_key := key_block[key_block_pos fixLen parameters.key_material_length];
key_block_pos +:= parameters.key_material_length;
client_initialization_vector := key_block[key_block_pos fixLen parameters.iv_size];
key_block_pos +:= parameters.iv_size;
server_initialization_vector := key_block[key_block_pos fixLen parameters.iv_size];
if parameters.isClient then
parameters.readMacSecret := server_write_MAC_secret;
parameters.writeMacSecret := client_write_MAC_secret;
parameters.readCipherState := setCipherKey(parameters.bulk_cipher_algorithm,
server_write_key, server_initialization_vector);
parameters.writeCipherState := setCipherKey(parameters.bulk_cipher_algorithm,
client_write_key, client_initialization_vector);
else
parameters.readMacSecret := client_write_MAC_secret;
parameters.writeMacSecret := server_write_MAC_secret;
parameters.readCipherState := setCipherKey(parameters.bulk_cipher_algorithm,
client_write_key, client_initialization_vector);
parameters.writeCipherState := setCipherKey(parameters.bulk_cipher_algorithm,
server_write_key, server_initialization_vector);
end if;
end func;
const func string: genRsaSignature (in string: signature, in digestAlgorithm: digestAlg) is func
result
var string: signatureString is "";
local
var algorithmIdentifierType: algorithmIdentifier is algorithmIdentifierType.value;
begin
case digestAlg of
when {SHA1}: algorithmIdentifier.algorithm := SHA1_OID;
when {SHA256}: algorithmIdentifier.algorithm := SHA256_OID;
when {SHA384}: algorithmIdentifier.algorithm := SHA384_OID;
when {SHA512}: algorithmIdentifier.algorithm := SHA512_OID;
end case;
signatureString := genAsn1Sequence(
genAlgorithmIdentifier(algorithmIdentifier) &
genAsn1Element(tagOctetString, signature));
end func;
const func boolean: verifySignature (in string: hashParameter, in integer: signatureScheme,
in string: signatureStri, in tlsParameters: parameters, inout tlsParseState: state) is func
result
var boolean: verified is FALSE;
local
var string: signatureHash is "";
var string: decryptedSignature is "";
var rsaSignatureType: rsaSignature is rsaSignatureType.value;
var ecdsaSignatureType: ecdsaSignature is ecdsaSignatureType.value;
begin
case signatureScheme of
when {RSA_PKCS1_MD5_SHA1}:
signatureHash := md5(hashParameter) & sha1(hashParameter);
verified := rsassaPkcs1V15Decrypt(parameters.publicRsaCertificateKey, signatureStri) = signatureHash;
when {RSA_PKCS1_SHA1, RSA_PKCS1_SHA224, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512}:
signatureHash := msgDigest(signatureHashByNumber[signatureScheme mdiv 256],
hashParameter);
decryptedSignature := rsassaPkcs1V15Decrypt(parameters.publicRsaCertificateKey, signatureStri);
rsaSignature := getRsaSignature(decryptedSignature);
verified := rsaSignature.signature = signatureHash;
when {ECDSA_SECP256R1_SHA256}:
signatureHash := sha256(hashParameter);
ecdsaSignature := getEcdsaSignature(signatureStri);
verified := verify(secp256r1, bytes2BigInt(signatureHash, UNSIGNED, BE),
ecdsaSignature, parameters.publicEccCertificateKey);
when {ECDSA_SHA1}:
signatureHash := sha1(hashParameter);
ecdsaSignature := getEcdsaSignature(signatureStri);
verified := verify(parameters.curve, bytes2BigInt(signatureHash, UNSIGNED, BE),
ecdsaSignature, parameters.publicEccCertificateKey);
end case;
end func;
const func string: genSignature (in string: hashParameter, in tlsParameters: parameters) is func
result
var string: signatureStri is "";
local
var string: signatureHash is "";
var string: rsaSignatureStri is "";
var digestAlgorithm: digestAlg is NO_DIGEST;
var ecdsaSignatureType: ecdsaSignature is ecdsaSignatureType.value;
begin
case parameters.signatureScheme of
when {RSA_PKCS1_MD5_SHA1}:
signatureHash := md5(hashParameter) & sha1(hashParameter);
signatureStri := rsassaPkcs1V15Encrypt(parameters.privateRsaCertificateKey, signatureHash);
when {RSA_PKCS1_SHA1, RSA_PKCS1_SHA224, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512}:
digestAlg := signatureHashByNumber[parameters.signatureScheme mdiv 256];
signatureHash := msgDigest(digestAlg, hashParameter);
rsaSignatureStri := genRsaSignature(signatureHash, digestAlg);
signatureStri := rsassaPkcs1V15Encrypt(parameters.privateRsaCertificateKey, rsaSignatureStri);
when {ECDSA_SECP256R1_SHA256}:
signatureHash := sha256(hashParameter);
ecdsaSignature := sign(secp256r1, bytes2BigInt(signatureHash, UNSIGNED, BE),
parameters.privateEccCertificateKey);
signatureStri := genAsn1Sequence(
genAsn1Element(tagInteger, bytes(ecdsaSignature.r, SIGNED, BE)) &
genAsn1Element(tagInteger, bytes(ecdsaSignature.s, SIGNED, BE)));
end case;
signatureStri := bytes(parameters.signatureScheme, UNSIGNED, BE, 2) &
bytes(length(signatureStri), UNSIGNED, BE, 2) &
signatureStri;
end func;
const proc: validateCertificates (in array x509Cert: cert) is func
local
var integer: index is 1;
var integer: issuerIndex is 1;
var string: commonName is "";
var certSubjectIndexHashType: certSubjectIndexHash is certSubjectIndexHashType.value;
var boolean: validated is FALSE;
begin
for key index range cert do
if time(NOW) >= cert[index].tbsCertificate.validity.notBefore and
time(NOW) <= cert[index].tbsCertificate.validity.notAfter then
if COMMON_NAME_OID in cert[index].tbsCertificate.subject then
if cert[index].tbsCertificate.subject[COMMON_NAME_OID] not in certSubjectIndexHash then
commonName := cert[index].tbsCertificate.subject[COMMON_NAME_OID];
certSubjectIndexHash @:= [commonName] index;
else
writeln("***** Second certificate with same common name.");
end if;
else
writeln("***** Certificate without common name.");
end if;
else
writeln("***** Certificate invalid now.");
end if;
end for;
repeat
index := issuerIndex;
if COMMON_NAME_OID in cert[index].tbsCertificate.issuer and
cert[index].tbsCertificate.issuer[COMMON_NAME_OID] in certSubjectIndexHash then
issuerIndex := certSubjectIndexHash[cert[index].tbsCertificate.issuer[COMMON_NAME_OID]];
validated := validateSignature(cert[index], cert[issuerIndex].tbsCertificate.subjectPublicKeyInfo);
else
issuerIndex := 0;
validated := FALSE;
end if;
until index = issuerIndex or not validated;
if index = 1 and issuerIndex = 1 then
writeln("Self signed certificate.");
elsif not validated then
writeln("***** Cannot be validated.");
end if;
end func;
const proc: processCertificate (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: sequenceLen is 0;
var integer: certLen is 0;
var integer: index is 1;
var array x509Cert: cert is 0 times x509Cert.value;
begin
startPos := state.pos;
state.pos +:= 4;
sequenceLen := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
while state.pos <= 3 + sequenceLen do
certLen := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
parameters.serverCertificates.certList &:= [] (state.message[state.pos len certLen]);
state.pos +:= certLen;
end while;
cert := length(parameters.serverCertificates.certList) times x509Cert.value;
for key index range parameters.serverCertificates.certList do
cert[index] := getX509Cert(parameters.serverCertificates.certList[index]);
end for;
parameters.publicRsaCertificateKey := cert[1].tbsCertificate.subjectPublicKeyInfo.publicRsaKey;
parameters.publicEccCertificateKey := cert[1].tbsCertificate.subjectPublicKeyInfo.publicEccKey;
if parameters.publicRsaCertificateKey.modulus = 0_ and
parameters.publicRsaCertificateKey.exponent = 0_ and
parameters.publicEccCertificateKey.x = 0_ and
parameters.publicEccCertificateKey.y = 0_ then
state.alert := BAD_CERTIFICATE;
end if;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processClientCertificate (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: sequenceLen is 0;
var integer: certLen is 0;
var integer: index is 1;
var array x509Cert: cert is 0 times x509Cert.value;
begin
startPos := state.pos;
state.pos +:= 4;
sequenceLen := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
while state.pos <= 3 + sequenceLen do
certLen := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
parameters.clientCertificates.certList &:= [] (state.message[state.pos len certLen]);
state.pos +:= certLen;
end while;
cert := length(parameters.clientCertificates.certList) times x509Cert.value;
for key index range parameters.clientCertificates.certList do
cert[index] := getX509Cert(parameters.clientCertificates.certList[index]);
end for;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processCertificateVerify (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
state.pos +:= length;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processEllipticCurvesExtension (inout tlsParameters: parameters,
in string: extensionData) is func
local
var integer: pos is 1;
var integer: length is 0;
var integer: curveNumber is 0;
begin
length := bytes2Int(extensionData[pos fixLen 2], UNSIGNED, BE);
pos +:= 2;
while pos < length(extensionData) and parameters.curve.bits = 0 do
curveNumber := bytes2Int(extensionData[pos fixLen 2], UNSIGNED, BE);
pos +:= 2;
if curveNumber >= minIdx(curveByNumber) and curveNumber <= maxIdx(curveByNumber) then
parameters.curve := curveByNumber[curveNumber];
end if;
end while;
end func;
const proc: processSignatureAlgorithmsExtension (inout tlsParameters: parameters,
in string: extensionData) is func
local
var integer: pos is 1;
var integer: length is 0;
var integer: signatureScheme is 0;
var integer: schemeFromList is 0;
begin
length := bytes2Int(extensionData[pos fixLen 2], UNSIGNED, BE);
pos +:= 2;
while pos < length(extensionData) and parameters.signatureScheme = 0 do
signatureScheme := bytes2Int(extensionData[pos fixLen 2], UNSIGNED, BE);
pos +:= 2;
if parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
for schemeFromList range serverSignatureSchemesEcdsa until parameters.signatureScheme <> 0 do
if schemeFromList = signatureScheme then
parameters.signatureScheme := signatureScheme;
end if;
end for;
else
for schemeFromList range serverSignatureSchemesRsa until parameters.signatureScheme <> 0 do
if schemeFromList = signatureScheme then
parameters.signatureScheme := signatureScheme;
end if;
end for;
end if;
end while;
end func;
const proc: processClientExtensions (inout tlsParameters: parameters, in string: extensions) is func
local
var integer: pos is 1;
var integer: extensionType is 0;
var integer: dataSize is 0;
var string: data is "";
begin
while pos < length(extensions) do
extensionType := bytes2Int(extensions[pos fixLen 2], UNSIGNED, BE);
pos +:= 2;
dataSize := bytes2Int(extensions[pos fixLen 2], UNSIGNED, BE);
pos +:= 2;
data := extensions[pos len dataSize];
pos +:= dataSize;
case extensionType of
when {ELLIPTIC_CURVES}:
processEllipticCurvesExtension(parameters, data);
when {SIGNATURE_ALGORITHMS}:
processSignatureAlgorithmsExtension(parameters, data);
end case;
end while;
end func;
const proc: processClientHello (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
var integer: beyond is 0;
var integer: sessionIdLen is 0;
var integer: numCipherSuites is 0;
var integer: numCompressionMethods is 0;
var integer: extensionBytes is 0;
var integer: index is 0;
var integer: cipher_suite_number is ord(TLS_NULL_WITH_NULL_NULL);
var integer: searchIndex is 0;
var integer: minIndex is succ(length(supportedCiphers));
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
beyond := state.pos + length;
if state.message[state.pos len 2] >= SSL_3_0 and
state.message[state.pos len 2] <= TLS_1_3 then
if state.message[state.pos len 2] < parameters.tls_version then
parameters.tls_version := state.message[state.pos len 2];
end if;
state.pos +:= 2;
parameters.client_random := state.message[state.pos len 32];
state.pos +:= 32;
sessionIdLen := ord(state.message[state.pos]);
incr(state.pos);
state.pos +:= sessionIdLen;
numCipherSuites := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE) div 2;
state.pos +:= 2;
for index range 1 to numCipherSuites do
cipher_suite_number := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
for key searchIndex range supportedCiphers do
if ord(supportedCiphers[searchIndex]) = cipher_suite_number and
searchIndex < minIndex then
minIndex := searchIndex;
end if;
end for;
state.pos +:= 2;
end for;
if minIndex <= length(supportedCiphers) then
parameters.cipher_suite := supportedCiphers[minIndex];
storeCipherSuite(parameters);
else
state.alert := HANDSHAKE_FAILURE;
end if;
numCompressionMethods := ord(state.message[state.pos]);
state.pos +:= 1 + numCompressionMethods;
if state.pos <= beyond - 2 then
extensionBytes := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
state.pos +:= 2;
processClientExtensions(parameters, state.message[state.pos len extensionBytes]);
state.pos +:= extensionBytes;
end if;
else
state.alert := PROCOCOL_VERSION;
end if;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processServerHello (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
var integer: beyond is 0;
var integer: sessionIdLen is 0;
var integer: extensionBytes is 0;
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
beyond := state.pos + length;
if state.message[state.pos len 2] >= SSL_3_0 and
state.message[state.pos len 2] <= parameters.tls_version then
parameters.tls_version := state.message[state.pos len 2];
state.pos +:= 2;
parameters.server_random := state.message[state.pos len 32];
state.pos +:= 32;
sessionIdLen := ord(state.message[state.pos]);
incr(state.pos);
parameters.session_id := state.message[state.pos len sessionIdLen];
state.pos +:= sessionIdLen;
parameters.cipher_suite := cipherSuite conv bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
storeCipherSuite(parameters);
state.pos +:= 2;
parameters.compression_algorithm := ord(state.message[state.pos]);
incr(state.pos);
if state.pos <= beyond - 2 then
extensionBytes := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
state.pos +:= 2 + extensionBytes;
end if;
else
state.alert := PROCOCOL_VERSION;
end if;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processServerKeyExchange (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
var integer: beyond is 0;
var integer: paramsStartPos is 0;
var integer: curveNumber is 0;
var integer: pointLength is 0;
var string: pointData is "";
var string: serverParams is "";
var integer: signatureScheme is 0;
var integer: signatureLength is 0;
var string: signatureStri is "";
var boolean: verified is FALSE;
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
beyond := state.pos + length;
if parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
if state.message[state.pos] = NAMED_CURVE then
paramsStartPos := state.pos;
incr(state.pos);
curveNumber := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
state.pos +:= 2;
if curveNumber < minIdx(curveByNumber) or curveNumber > maxIdx(curveByNumber) then
state.alert := ILLEGAL_PARAMETER;
else
parameters.curve := curveByNumber[curveNumber];
pointLength := ord(state.message[state.pos]);
incr(state.pos);
pointData := state.message[state.pos len pointLength];
parameters.publicEccKeyOfServer := ecPointDecode(parameters.curve, pointData);
state.pos +:= pointLength;
serverParams := state.message[paramsStartPos .. pred(state.pos)];
signatureScheme := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
state.pos +:= 2;
if parameters.tls_version >= TLS_1_2 then
signatureLength := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
state.pos +:= 2;
else
signatureLength := beyond - state.pos;
end if;
signatureStri := state.message[state.pos len signatureLength];
state.pos +:= signatureLength;
if not verifySignature(parameters.client_random &
parameters.server_random &
serverParams, signatureScheme, signatureStri,
parameters, state) then
state.alert := HANDSHAKE_FAILURE;
end if;
end if;
else
state.alert := ILLEGAL_PARAMETER;
end if;
end if;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processCertificateRequest (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
state.pos +:= length;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processServerHelloDone (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processClientKeyExchange (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
var integer: encryptedSecretLength is 0;
var string: encryptedPreMasterSecret is "";
var string: preMasterSecret is "";
var integer: pointLength is 0;
var string: pointData is "";
var ecPoint: publicEccKeyOfClient is ecPoint.value;
var ecPoint: sharedSecretEcPoint is ecPoint.value;
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
if parameters.key_exchange_algorithm = RSA then
if parameters.tls_version = SSL_3_0 then
encryptedSecretLength := length;
else
encryptedSecretLength := bytes2Int(state.message[state.pos fixLen 2], UNSIGNED, BE);
state.pos +:= 2;
end if;
encryptedPreMasterSecret := state.message[state.pos len encryptedSecretLength];
state.pos +:= encryptedSecretLength;
preMasterSecret := rsaesPkcs1V15Decrypt(parameters.privateRsaCertificateKey,
encryptedPreMasterSecret);
elsif parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
pointLength := ord(state.message[state.pos]);
incr(state.pos);
pointData := state.message[state.pos len pointLength];
publicEccKeyOfClient := ecPointDecode(parameters.curve, pointData);
state.pos +:= pointLength;
sharedSecretEcPoint := multFast(parameters.curve, publicEccKeyOfClient,
parameters.ownEccKeyPair.privateKey);
preMasterSecret := bytes(sharedSecretEcPoint.x, UNSIGNED, BE,
getSizeInBytes(parameters.curve));
end if;
computeMasterSecret(parameters, preMasterSecret);
storeKeys(parameters);
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: processChangeCipherSpec (inout tlsParameters: parameters, inout tlsParseState: state) is func
begin
incr(state.pos);
end func;
const proc: processFinished (inout tlsParameters: parameters, inout tlsParseState: state) is func
local
var integer: startPos is 0;
var integer: length is 0;
var string: finished_label is "";
var string: verify_data is "";
var string: handshake_hash is "";
var string: computed_verify_data is "";
begin
startPos := state.pos;
incr(state.pos);
length := bytes2Int(state.message[state.pos fixLen 3], UNSIGNED, BE);
state.pos +:= 3;
verify_data := state.message[state.pos len length];
state.pos +:= length;
if parameters.tls_version = SSL_3_0 then
if parameters.isClient then
finished_label := "SRVR";
else
finished_label := "CLNT";
end if;
computed_verify_data := md5(parameters.master_secret & MD5_PAD2 &
md5(parameters.handshake_messages & finished_label &
parameters.master_secret & MD5_PAD1)) &
sha1(parameters.master_secret & SHA_PAD2 &
sha1(parameters.handshake_messages & finished_label &
parameters.master_secret & SHA_PAD1));
else
if parameters.isClient then
finished_label := "server finished";
else
finished_label := "client finished";
end if;
if parameters.tls_version >= TLS_1_2 then
computed_verify_data := p_hash(SHA256, parameters.master_secret, finished_label &
sha256(parameters.handshake_messages), 12);
else
handshake_hash := md5(parameters.handshake_messages) &
sha1(parameters.handshake_messages);
computed_verify_data := pseudoRandomFunction(parameters.master_secret, finished_label,
handshake_hash, 12);
end if;
end if;
if verify_data <> computed_verify_data then
writeln(" ***** Handshake not verified");
raise RANGE_ERROR;
end if;
parameters.handshake_messages &:= state.message[startPos .. pred(state.pos)];
end func;
const proc: getTlsMsgRecord (inout file: sock, inout tlsParseState: state) is func
local
var integer: missing is 0;
var string: msg2 is "";
begin
if state.pos > length(state.message) then
state.message := gets(sock, 5);
if length(state.message) = 5 and
state.message[1] >= CHANGE_CIPHER_SPEC and state.message[1] <= APPLICATION_DATA then
state.contentType := state.message[1];
state.length := bytes2Int(state.message[4 fixLen 2], UNSIGNED, BE);
missing := state.length;
repeat
msg2 := gets(sock, missing);
state.message &:= msg2;
missing -:= length(msg2);
until missing = 0 or eof(sock);
if missing = 0 then
state.pos := 6;
else
state.contentType := NO_MESSAGE;
state.length := 0;
end if;
else
state.contentType := NO_MESSAGE;
state.length := 0;
end if;
end if;
end func;
const proc: loadCompleteHandshakeMsg (inout file: sock, inout tlsParseState: state) is func
local
var integer: lengthOfHandshakeMsg is 0;
var integer: remainingBytesInMsgRecord is 0;
var integer: totallyMissing is 0;
var string: stri is "";
var integer: recordLength is 0;
var integer: missing is 0;
var string: msg2 is "";
begin
if length(state.message) >= state.pos + 3 then
lengthOfHandshakeMsg := bytes2Int(state.message[state.pos + 1 fixLen 3], UNSIGNED, BE);
remainingBytesInMsgRecord := length(state.message) - state.pos - 3;
if lengthOfHandshakeMsg > remainingBytesInMsgRecord then
totallyMissing := lengthOfHandshakeMsg - remainingBytesInMsgRecord;
repeat
stri := gets(sock, 5);
if length(stri) = 5 and stri[1] = HANDSHAKE then
recordLength := bytes2Int(stri[4 fixLen 2], UNSIGNED, BE);
missing := recordLength;
repeat
msg2 := gets(sock, missing);
state.message &:= msg2;
missing -:= length(msg2);
until missing = 0 or eof(sock);
totallyMissing -:= recordLength;
else
state.alert := UNEXPECTED_MESSAGE;
end if;
until totallyMissing <= 0 or eof(sock);
end if;
end if;
end func;
const func string: genExtension (in integer: extensionType, in string: data) is
return bytes(extensionType, UNSIGNED, BE, 2) &
bytes(length(data), UNSIGNED, BE, 2) & data;
const func string: serverNameExtension (in string: serverName) is
return bytes(length(serverName) + 3, UNSIGNED, BE, 2) & "\0;" &
bytes(length(serverName), UNSIGNED, BE, 2) & serverName;
const func string: genEllipticCurvesExtension (in array ellipticCurve: curves) is func
result
var string: extensionBytes is "";
local
var integer: curveNumber is 0;
begin
extensionBytes := bytes(length(curves) * 2, UNSIGNED, BE, 2);
for key curveNumber range curves do
extensionBytes &:= bytes(curveNumber, UNSIGNED, BE, 2);
end for;
end func;
const func string: int16BeArrayExtension (in array integer: intArray) is func
result
var string: extensionBytes is "";
local
var integer: intValue is 0;
begin
extensionBytes := bytes(length(intArray) * 2, UNSIGNED, BE, 2);
for intValue range intArray do
extensionBytes &:= bytes(intValue, UNSIGNED, BE, 2);
end for;
end func;
const func string: genClientExtensions (in tlsParameters: parameters) is func
result
var string: extensionBytes is "";
begin
if parameters.hostName <> "" then
extensionBytes &:= genExtension(SERVER_NAME, serverNameExtension(parameters.hostName));
end if;
extensionBytes &:= genExtension(ELLIPTIC_CURVES, genEllipticCurvesExtension(curveByNumber));
extensionBytes &:= genExtension(SIGNATURE_ALGORITHMS, int16BeArrayExtension(signatureSchemes));
if extensionBytes <> "" then
extensionBytes := bytes(length(extensionBytes), UNSIGNED, BE, 2) & extensionBytes;
end if;
end func;
const func string: genClientHello (inout tlsParameters: parameters, in string: sessionId) is func
result
var string: clientHello is "";
local
var integer: length is 0;
var integer: count is 0;
var cipherSuite: cipher is TLS_NULL_WITH_NULL_NULL;
begin
parameters.client_random :=
bytes(timestamp1970(time(NOW)), UNSIGNED, BE, 4) &
bytes(rand(0_, 2_ ** (28 * 8) - 1_), UNSIGNED, BE, 28);
clientHello := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(CLIENT_HELLO) &
"\0;\0;\0;" &
parameters.tls_version &
parameters.client_random &
str(chr(length(sessionId))) &
sessionId &
bytes(2 * length(supportedCiphers), UNSIGNED, BE, 2);
for cipher range supportedCiphers do
clientHello &:= bytes(ord(cipher), UNSIGNED, BE, 2);
end for;
clientHello &:= "\1;" &
"\0;";
clientHello &:= genClientExtensions(parameters);
length := length(clientHello);
clientHello @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
clientHello @:= [8] bytes(length - 9, UNSIGNED, BE, 2);
parameters.handshake_messages &:= clientHello[6 ..];
end func;
const func string: genServerHello (inout tlsParameters: parameters) is func
result
var string: serverHello is "";
local
const integer: SESSION_ID_LEN is 32;
var integer: length is 0;
var integer: count is 0;
begin
parameters.server_random :=
bytes(timestamp1970(time(NOW)), UNSIGNED, BE, 4) &
bytes(rand(0_, 2_ ** (28 * 8) - 1_), UNSIGNED, BE, 28);
parameters.session_id :=
bytes(rand(0_, 2_ ** (SESSION_ID_LEN * 8) - 1_), UNSIGNED, BE, SESSION_ID_LEN);
serverHello := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(SERVER_HELLO) &
"\0;\0;\0;" &
parameters.tls_version &
parameters.server_random &
str(chr(SESSION_ID_LEN)) &
parameters.session_id &
bytes(ord(parameters.cipher_suite), UNSIGNED, BE, 2) &
"\0;";
length := length(serverHello);
serverHello @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
serverHello @:= [8] bytes(length - 9, UNSIGNED, BE, 2);
parameters.handshake_messages &:= serverHello[6 ..];
end func;
const func string: genCertificate (inout tlsParameters: parameters, in array string: certList) is func
result
var string: certificate is "";
local
var integer: length is 0;
var integer: index is 0;
begin
certificate := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(CERTIFICATE) &
"\0;\0;\0;" &
"\0;\0;\0;";
for key index range certList do
certificate &:= "\0;" &
bytes(length(certList[index]), UNSIGNED, BE, 2) &
certList[index];
end for;
length := length(certificate);
certificate @:= [ 4] bytes(length - 5, UNSIGNED, BE, 2);
certificate @:= [ 8] bytes(length - 9, UNSIGNED, BE, 2);
certificate @:= [11] bytes(length - 12, UNSIGNED, BE, 2);
parameters.handshake_messages &:= certificate[6 ..];
end func;
const func string: genServerKeyExchange (inout tlsParameters: parameters) is func
result
var string: serverKeyExchange is "";
local
var integer: length is 0;
var string: serverParams is "";
var string: pointData is "";
var string: signatureHash is "";
var string: signatureStri is "";
begin
serverKeyExchange := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(SERVER_KEY_EXCHANGE) &
"\0;\0;\0;";
if parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
parameters.ownEccKeyPair := genEccKeyPair(parameters.curve);
pointData := ecPointEncode(parameters.curve, parameters.ownEccKeyPair.publicKey);
serverParams &:= str(NAMED_CURVE) &
bytes(getEllipticCurveNumber(parameters.curve), UNSIGNED, BE, 2) &
str(chr(length(pointData))) & pointData;
signatureStri := genSignature(parameters.client_random &
parameters.server_random &
serverParams, parameters);
serverKeyExchange &:= serverParams & signatureStri;
end if;
length := length(serverKeyExchange);
serverKeyExchange @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
serverKeyExchange @:= [8] bytes(length - 9, UNSIGNED, BE, 2);
parameters.handshake_messages &:= serverKeyExchange[6 ..];
end func;
const func string: genCertificateRequest (inout tlsParameters: parameters) is func
result
var string: certificateRequest is "";
local
var integer: length is 0;
var integer: signatureAlgorithm is 0;
begin
certificateRequest := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(CERTIFICATE_REQUEST) &
"\0;\0;\0;" &
"\1;" &
"\1;";
if parameters.tls_version >= TLS_1_2 then
if parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
certificateRequest &:= bytes(length(serverSignatureSchemesEcdsa) * 2, UNSIGNED, BE, 2);
for signatureAlgorithm range serverSignatureSchemesEcdsa do
certificateRequest &:= bytes(signatureAlgorithm, UNSIGNED, BE, 2);
end for;
else
certificateRequest &:= bytes(length(serverSignatureSchemesRsa) * 2, UNSIGNED, BE, 2);
for signatureAlgorithm range serverSignatureSchemesRsa do
certificateRequest &:= bytes(signatureAlgorithm, UNSIGNED, BE, 2);
end for;
end if;
end if;
certificateRequest &:= "\0;\0;";
length := length(certificateRequest);
certificateRequest @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
certificateRequest @:= [8] bytes(length - 9, UNSIGNED, BE, 2);
parameters.handshake_messages &:= certificateRequest[6 ..];
end func;
const func string: genServerHelloDone (inout tlsParameters: parameters) is func
result
var string: serverHelloDone is "";
local
var integer: length is 0;
begin
serverHelloDone := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(SERVER_HELLO_DONE) &
"\0;\0;\0;";
length := length(serverHelloDone);
serverHelloDone @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
parameters.handshake_messages &:= serverHelloDone[6 ..];
end func;
const func string: genClientKeyExchange (inout tlsParameters: parameters) is func
result
var string: clientKeyExchange is "";
local
var integer: length is 0;
var string: preMasterSecret is "";
var string: encryptedPreMasterSecret is "";
var string: pointData is "";
var ecPoint: sharedSecretEcPoint is ecPoint.value;
begin
clientKeyExchange := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(CLIENT_KEY_EXCHANGE) &
"\0;\0;\0;";
if parameters.key_exchange_algorithm = RSA then
preMasterSecret :=
parameters.tls_version &
bytes(rand(0_, 2_ ** (46 * 8) - 1_), UNSIGNED, BE, 46);
encryptedPreMasterSecret := rsaesPkcs1V15Encrypt(parameters.publicRsaCertificateKey,
preMasterSecret);
if parameters.tls_version <> SSL_3_0 then
clientKeyExchange &:= bytes(length(encryptedPreMasterSecret), UNSIGNED, BE, 2);
end if;
clientKeyExchange &:= encryptedPreMasterSecret;
elsif parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
parameters.ownEccKeyPair := genEccKeyPair(parameters.curve);
pointData := ecPointEncode(parameters.curve, parameters.ownEccKeyPair.publicKey);
clientKeyExchange &:= str(chr(length(pointData))) & pointData;
sharedSecretEcPoint := multFast(parameters.curve, parameters.publicEccKeyOfServer,
parameters.ownEccKeyPair.privateKey);
preMasterSecret := bytes(sharedSecretEcPoint.x, UNSIGNED, BE,
getSizeInBytes(parameters.curve));
end if;
computeMasterSecret(parameters, preMasterSecret);
storeKeys(parameters);
length := length(clientKeyExchange);
clientKeyExchange @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
clientKeyExchange @:= [8] bytes(length - 9, UNSIGNED, BE, 2);
parameters.handshake_messages &:= clientKeyExchange[6 ..];
end func;
const func string: genChangeCipherSpec (in tlsParameters: parameters) is func
result
var string: changeCipherSpec is "";
local
var integer: length is 0;
begin
changeCipherSpec := str(CHANGE_CIPHER_SPEC) &
parameters.tls_version &
"\0;\0;" &
"\1;";
length := length(changeCipherSpec);
changeCipherSpec @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
end func;
const func string: genFinished (inout tlsParameters: parameters) is func
result
var string: finished is "";
local
var integer: length is 0;
var string: finished_label is "";
var string: verify_data is "";
begin
finished := str(HANDSHAKE) &
parameters.tls_version &
"\0;\0;" &
str(FINISHED) &
"\0;\0;\0;";
if parameters.tls_version = SSL_3_0 then
if parameters.isClient then
finished_label := "CLNT";
else
finished_label := "SRVR";
end if;
verify_data := md5(parameters.master_secret & MD5_PAD2 &
md5(parameters.handshake_messages & finished_label &
parameters.master_secret & MD5_PAD1)) &
sha1(parameters.master_secret & SHA_PAD2 &
sha1(parameters.handshake_messages & finished_label &
parameters.master_secret & SHA_PAD1));
else
if parameters.isClient then
finished_label := "client finished";
else
finished_label := "server finished";
end if;
if parameters.tls_version >= TLS_1_2 then
verify_data := p_hash(SHA256, parameters.master_secret, finished_label &
sha256(parameters.handshake_messages), 12);
else
verify_data := pseudoRandomFunction(parameters.master_secret, finished_label,
md5(parameters.handshake_messages) &
sha1(parameters.handshake_messages), 12);
end if;
end if;
finished &:= verify_data;
length := length(finished);
finished @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
finished @:= [8] bytes(length - 9, UNSIGNED, BE, 2);
parameters.handshake_messages &:= finished[6 ..];
end func;
const func string: genAlert (inout tlsParameters: parameters, in char: description) is func
result
var string: alert is "";
local
var integer: length is 0;
begin
alert := str(ALERT) &
parameters.tls_version &
"\0;\0;" &
"\1;" &
str(description);
length := length(alert);
alert @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
end func;
const func string: tlsEncryptRecord (inout tlsParameters: parameters, in string: plain) is func
result
var string: encrypted is "";
local
var string: content is "";
var string: iv is "";
var string: mac is "";
var integer: padding_length is 0;
var string: padding is "";
var string: encoded is "";
var integer: length is 0;
begin
encrypted := plain[.. 3] &
"\0;\0;";
if parameters.tls_version = SSL_3_0 then
mac := msgDigest(parameters.mac_algorithm, parameters.writeMacSecret & SHA_PAD2 &
msgDigest(parameters.mac_algorithm, parameters.writeMacSecret & SHA_PAD1 &
bytes(parameters.writeSequenceNumber, UNSIGNED, BE, 8) &
plain[1 len 1] & plain[4 ..]));
else
mac := hmac(parameters.mac_algorithm, parameters.writeMacSecret,
bytes(parameters.writeSequenceNumber, UNSIGNED, BE, 8) & plain);
end if;
content := plain[6 ..];
if parameters.block_size <> 0 then
if parameters.tls_version >= TLS_1_1 then
iv := bytes(rand(0_, 2_ ** (parameters.block_size * 8) - 1_), UNSIGNED, BE,
parameters.block_size);
end if;
padding_length := parameters.block_size - 1 -
(length(content) + length(mac)) mod parameters.block_size;
padding := str(chr(padding_length)) mult succ(padding_length);
end if;
initAead(parameters.writeCipherState, plain[.. 3], parameters.writeSequenceNumber);
encoded := encode(parameters.writeCipherState, iv & content & mac & padding);
encrypted &:= encoded;
length := length(encrypted);
encrypted @:= [4] bytes(length - 5, UNSIGNED, BE, 2);
incr(parameters.writeSequenceNumber);
end func;
const func boolean: tlsDecryptRecord (inout tlsParameters: parameters, inout tlsParseState: state) is func
result
var boolean: decryptOkay is TRUE;
local
var string: version is "";
var string: decoded is "";
var integer: padding_length is 0;
var string: content is "";
var string: mac is "";
var string: verify is "";
var string: plain is "";
begin
version := state.message[2 len 2];
initAead(parameters.readCipherState, state.message[.. 3], parameters.readSequenceNumber);
decoded := decode(parameters.readCipherState, state.message[state.pos len state.length]);
if parameters.block_size <> 0 then
padding_length := ord(decoded[length(decoded)]);
if padding_length < length(decoded) and
decoded[length(decoded) - padding_length ..] =
str(chr(padding_length)) mult succ(padding_length) then
decoded := decoded[.. length(decoded) - succ(padding_length)];
if version >= TLS_1_1 then
decoded := decoded[succ(parameters.block_size) ..];
end if;
else
decryptOkay := FALSE;
state.alert := DECRYPTION_FAILED;
end if;
end if;
if decryptOkay then
content := decoded[.. length(decoded) - digestSize(parameters.mac_algorithm)];
mac := decoded[length(decoded) - digestSize(parameters.mac_algorithm) + 1 ..];
plain := state.message[.. 3] &
bytes(length(content), UNSIGNED, BE, 2) &
content;
if parameters.bulk_cipher_algorithm = AES_GCM then
mac := getMac(parameters.readCipherState);
verify := getComputedMac(parameters.readCipherState);
else
if version = SSL_3_0 then
verify := msgDigest(parameters.mac_algorithm, parameters.readMacSecret & SHA_PAD2 &
msgDigest(parameters.mac_algorithm, parameters.readMacSecret & SHA_PAD1 &
bytes(parameters.readSequenceNumber, UNSIGNED, BE, 8) &
plain[1 len 1] & plain[4 ..]));
else
verify := hmac(parameters.mac_algorithm, parameters.readMacSecret,
bytes(parameters.readSequenceNumber, UNSIGNED, BE, 8) & plain);
end if;
end if;
if mac = verify then
state.length := length(content);
state.message := plain;
else
decryptOkay := FALSE;
state.alert := BAD_RECORD_MAC;
end if;
end if;
incr(parameters.readSequenceNumber);
end func;
const proc: sendAlertAndClose (inout tlsFile: aFile, in char: alertDescription) is func
local
var string: alert is "";
begin
alert := genAlert(aFile.parameters, alertDescription);
if aFile.parameters.writeEncryptedRecords then
alert := tlsEncryptRecord(aFile.parameters, alert);
end if;
block
write(aFile.sock, alert);
exception
catch FILE_ERROR: noop;
end block;
close(aFile.sock);
aFile.sock := STD_NULL;
aFile.parseState.pos := succ(length(aFile.parseState.message));
end func;
const proc: updateClientCache (in tlsParameters: parameters, in socketAddress: address) is func
local
var clientSession: session is clientSession.value;
begin
if parameters.session_id <> "" then
session.session_id := parameters.session_id;
session.bulk_cipher_algorithm := parameters.bulk_cipher_algorithm;
session.master_secret := parameters.master_secret;
session.last_use := time(NOW);
clientSessionCache @:= [address] session;
end if;
end func;
const func file: negotiateSecurityParameters (inout tlsFile: new_file) is func
result
var file: tlsSock is STD_NULL;
local
var string: clientKeyExchange is "";
var string: changeCipherSpec is "";
var string: finished is "";
var boolean: serverHelloDone is FALSE;
var boolean: unexpectedMessage is FALSE;
begin
repeat
getTlsMsgRecord(new_file.sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE then
loadCompleteHandshakeMsg(new_file.sock, new_file.parseState);
if new_file.parseState.alert = CLOSE_NOTIFY then
if new_file.parseState.message[new_file.parseState.pos] = CERTIFICATE then
processCertificate(new_file.parameters, new_file.parseState);
elsif new_file.parseState.message[new_file.parseState.pos] = SERVER_KEY_EXCHANGE then
processServerKeyExchange(new_file.parameters, new_file.parseState);
elsif new_file.parseState.message[new_file.parseState.pos] = CERTIFICATE_REQUEST then
processCertificateRequest(new_file.parameters, new_file.parseState);
elsif new_file.parseState.message[new_file.parseState.pos] = SERVER_HELLO_DONE then
processServerHelloDone(new_file.parameters, new_file.parseState);
serverHelloDone := TRUE;
else
unexpectedMessage := TRUE;
end if;
else
unexpectedMessage := TRUE;
end if;
else
unexpectedMessage := TRUE;
end if;
until serverHelloDone or unexpectedMessage;
if serverHelloDone then
clientKeyExchange := genClientKeyExchange(new_file.parameters);
write(new_file.sock, clientKeyExchange);
changeCipherSpec := genChangeCipherSpec(new_file.parameters);
write(new_file.sock, changeCipherSpec);
new_file.parameters.writeEncryptedRecords := TRUE;
finished := genFinished(new_file.parameters);
finished := tlsEncryptRecord(new_file.parameters, finished);
write(new_file.sock, finished);
repeat
getTlsMsgRecord(new_file.sock, new_file.parseState);
until new_file.parseState.contentType = CHANGE_CIPHER_SPEC or
new_file.parseState.contentType = ALERT or
new_file.parseState.contentType = NO_MESSAGE;
if new_file.parseState.contentType = CHANGE_CIPHER_SPEC then
processChangeCipherSpec(new_file.parameters, new_file.parseState);
getTlsMsgRecord(new_file.sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE then
if tlsDecryptRecord(new_file.parameters, new_file.parseState) then
if new_file.parseState.message[new_file.parseState.pos] = FINISHED then
processFinished(new_file.parameters, new_file.parseState);
updateClientCache(new_file.parameters, peerAddress(new_file.sock));
tlsSock := toInterface(new_file);
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
sendAlertAndClose(new_file, new_file.parseState.alert);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
if new_file.parseState.alert = CLOSE_NOTIFY then
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
else
sendAlertAndClose(new_file, new_file.parseState.alert);
end if;
end if;
end func;
const func file: openNewTlsSocket (inout file: sock, in string: hostName) is func
result
var file: tlsSock is STD_NULL;
local
var tlsFile: new_file is tlsFile.value;
var string: clientHello is "";
begin
new_file.sock := sock;
new_file.parameters.isClient := TRUE;
new_file.parameters.hostName := hostName;
clientHello := genClientHello(new_file.parameters, "");
write(sock, clientHello);
getTlsMsgRecord(sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE and
new_file.parseState.message[new_file.parseState.pos] = SERVER_HELLO then
processServerHello(new_file.parameters, new_file.parseState);
if new_file.parseState.alert <> CLOSE_NOTIFY then
sendAlertAndClose(new_file, new_file.parseState.alert);
else
tlsSock := negotiateSecurityParameters(new_file);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
end func;
const func file: openTlsSocket (inout file: sock, in clientSession: session,
in string: hostName) is func
result
var file: tlsSock is STD_NULL;
local
var tlsFile: new_file is tlsFile.value;
var socketAddress: peerAddress is socketAddress.value;
var string: sessionId is "";
var string: clientHello is "";
var string: changeCipherSpec is "";
var string: finished is "";
begin
if sock <> STD_NULL then
peerAddress := peerAddress(sock);
new_file.sock := sock;
new_file.parameters.isClient := TRUE;
new_file.parameters.hostName := hostName;
if session.last_use + clientCacheValid > time(NOW) then
sessionId := session.session_id;
end if;
clientHello := genClientHello(new_file.parameters, sessionId);
write(sock, clientHello);
getTlsMsgRecord(sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE and
new_file.parseState.message[new_file.parseState.pos] = SERVER_HELLO then
processServerHello(new_file.parameters, new_file.parseState);
if new_file.parseState.alert <> CLOSE_NOTIFY then
sendAlertAndClose(new_file, new_file.parseState.alert);
elsif new_file.parameters.session_id <> sessionId or
new_file.parameters.bulk_cipher_algorithm <> session.bulk_cipher_algorithm then
tlsSock := negotiateSecurityParameters(new_file);
else
getTlsMsgRecord(new_file.sock, new_file.parseState);
if new_file.parseState.contentType = CHANGE_CIPHER_SPEC then
processChangeCipherSpec(new_file.parameters, new_file.parseState);
new_file.parameters.master_secret := session.master_secret;
storeKeys(new_file.parameters);
getTlsMsgRecord(new_file.sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE then
if tlsDecryptRecord(new_file.parameters, new_file.parseState) then
if new_file.parseState.message[new_file.parseState.pos] = FINISHED then
processFinished(new_file.parameters, new_file.parseState);
changeCipherSpec := genChangeCipherSpec(new_file.parameters);
write(new_file.sock, changeCipherSpec);
new_file.parameters.writeEncryptedRecords := TRUE;
finished := genFinished(new_file.parameters);
finished := tlsEncryptRecord(new_file.parameters, finished);
write(new_file.sock, finished);
updateClientCache(new_file.parameters, peerAddress);
tlsSock := toInterface(new_file);
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
sendAlertAndClose(new_file, new_file.parseState.alert);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
if tlsSock = STD_NULL then
excl(clientSessionCache, peerAddress);
sock := openSocket(peerAddress);
tlsSock := openNewTlsSocket(sock, hostName);
end if;
end if;
end func;
const func file: openTlsSocket (inout file: sock, in string: hostName) is func
result
var file: tlsSock is STD_NULL;
begin
if sock <> STD_NULL then
if peerAddress(sock) in clientSessionCache then
tlsSock := openTlsSocket(sock, clientSessionCache[peerAddress(sock)],
hostName);
else
tlsSock := openNewTlsSocket(sock, hostName);
end if;
end if;
end func;
const func file: openTlsSocket (inout file: sock) is
return openTlsSocket (sock, "");
const func file: openTlsSocket (in string: hostName, in integer: portNumber) is func
result
var file: tlsSock is STD_NULL;
local
var file: sock is STD_NULL;
begin
sock := openInetSocket(hostName, portNumber);
tlsSock := openTlsSocket(sock, hostName);
end func;
const func file: openServerTls (inout file: sock, in certAndKey: certificateAndKey) is func
result
var file: tlsSock is STD_NULL;
local
var tlsFile: new_file is tlsFile.value;
var string: clientHello is "";
var string: serverHello is "";
var string: certificate is "";
var string: serverKeyExchange is "";
var string: certificateRequest is "";
var string: serverHelloDone is "";
var string: changeCipherSpec is "";
var string: finished is "";
var boolean: okay is TRUE;
begin
if sock <> STD_NULL then
new_file.sock := sock;
new_file.parameters.isClient := FALSE;
new_file.parameters.privateRsaCertificateKey := certificateAndKey.privateRsaKey;
new_file.parameters.privateEccCertificateKey := certificateAndKey.privateEccKey;
getTlsMsgRecord(sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE and
new_file.parseState.message[new_file.parseState.pos] = CLIENT_HELLO then
processClientHello(new_file.parameters, new_file.parseState);
if new_file.parseState.alert <> CLOSE_NOTIFY then
sendAlertAndClose(new_file, new_file.parseState.alert);
okay := FALSE;
else
serverHello := genServerHello(new_file.parameters);
write(sock, serverHello);
certificate := genCertificate(new_file.parameters, certificateAndKey.certList);
block
write(sock, certificate);
exception
catch FILE_ERROR:
okay := FALSE;
end block;
if okay and new_file.parameters.key_exchange_algorithm = EC_DIFFIE_HELLMAN then
serverKeyExchange := genServerKeyExchange(new_file.parameters);
write(sock, serverKeyExchange);
end if;
end if;
if okay then
serverHelloDone := genServerHelloDone(new_file.parameters);
write(sock, serverHelloDone);
repeat
getTlsMsgRecord(sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE then
if new_file.parseState.message[new_file.parseState.pos] = CLIENT_KEY_EXCHANGE then
processClientKeyExchange(new_file.parameters, new_file.parseState);
elsif new_file.parseState.message[new_file.parseState.pos] = CERTIFICATE then
processClientCertificate(new_file.parameters, new_file.parseState);
elsif new_file.parseState.message[new_file.parseState.pos] = CERTIFICATE_VERIFY then
processCertificateVerify(new_file.parameters, new_file.parseState);
end if;
end if;
until new_file.parseState.contentType = CHANGE_CIPHER_SPEC or
new_file.parseState.contentType = ALERT or
new_file.parseState.contentType = NO_MESSAGE;
if new_file.parseState.contentType = CHANGE_CIPHER_SPEC then
processChangeCipherSpec(new_file.parameters, new_file.parseState);
getTlsMsgRecord(sock, new_file.parseState);
if new_file.parseState.contentType = HANDSHAKE then
if tlsDecryptRecord(new_file.parameters, new_file.parseState) then
if new_file.parseState.message[new_file.parseState.pos] = FINISHED then
processFinished(new_file.parameters, new_file.parseState);
changeCipherSpec := genChangeCipherSpec(new_file.parameters);
write(sock, changeCipherSpec);
new_file.parameters.writeEncryptedRecords := TRUE;
finished := genFinished(new_file.parameters);
finished := tlsEncryptRecord(new_file.parameters, finished);
block
write(sock, finished);
exception
catch FILE_ERROR:
okay := FALSE;
end block;
if okay then
tlsSock := toInterface(new_file);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
sendAlertAndClose(new_file, new_file.parseState.alert);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
end if;
else
sendAlertAndClose(new_file, UNEXPECTED_MESSAGE);
end if;
end if;
end func;
const proc: close (inout tlsFile: aFile) is func
begin
sendAlertAndClose(aFile, CLOSE_NOTIFY);
end func;
const func boolean: eof (in tlsFile: inFile) is
return inFile.bufferChar = EOF;
const func string: getApplicationData (inout tlsFile: inFile) is func
result
var string: applicationData is "";
begin
getTlsMsgRecord(inFile.sock, inFile.parseState);
if inFile.parseState.contentType = APPLICATION_DATA then
if tlsDecryptRecord(inFile.parameters, inFile.parseState) then
applicationData := inFile.parseState.message[inFile.parseState.pos ..];
inFile.parseState.pos +:= inFile.parseState.length;
else
sendAlertAndClose(inFile, inFile.parseState.alert);
end if;
elsif inFile.parseState.contentType = ALERT then
if tlsDecryptRecord(inFile.parameters, inFile.parseState) then
close(inFile);
else
sendAlertAndClose(inFile, inFile.parseState.alert);
end if;
elsif inFile.parseState.contentType = CLIENT_HELLO then
sendAlertAndClose(inFile, NO_RENEGOTIATION);
elsif inFile.parseState.contentType = NO_MESSAGE then
close(inFile);
else
sendAlertAndClose(inFile, UNEXPECTED_MESSAGE);
end if;
end func;
const proc: write (inout tlsFile: outFile, in string: stri) is func
local
const integer: maxStriLen is 2**14 - 1;
var integer: startIndex is 1;
var string: plain is "";
var string: message is "";
begin
repeat
plain := str(APPLICATION_DATA) &
outFile.parameters.tls_version &
"\0;\0;" &
stri[startIndex len maxStriLen];
plain @:= [4] bytes(length(plain) - 5, UNSIGNED, BE, 2);
message := tlsEncryptRecord(outFile.parameters, plain);
write(outFile.sock, message);
startIndex +:= maxStriLen;
until startIndex > length(stri);
end func;
const proc: writeln (inout tlsFile: outFile, in string: stri) is func
begin
write(outFile, stri & "\n");
end func;
const func string: gets (inout tlsFile: inFile, in integer: maxLength) is func
result
var string: striRead is "";
begin
if maxLength <= 0 then
if maxLength <> 0 then
raise RANGE_ERROR;
end if;
else
if inFile.readBuffer = "" and not eof(inFile.sock) then
inFile.readBuffer := getApplicationData(inFile);
end if;
if length(inFile.readBuffer) > maxLength then
striRead := inFile.readBuffer[.. maxLength];
inFile.readBuffer := inFile.readBuffer[succ(maxLength) ..];
else
striRead := inFile.readBuffer;
inFile.readBuffer := "";
end if;
if striRead = "" and eof(inFile.sock) then
inFile.bufferChar := EOF;
end if;
end if;
end func;
const func string: getln (inout tlsFile: inFile) is func
result
var string: stri is "";
local
var integer: nlPos is 0;
begin
nlPos := pos(inFile.readBuffer, '\n');
while nlPos = 0 and not eof(inFile.sock) do
inFile.readBuffer &:= getApplicationData(inFile);
nlPos := pos(inFile.readBuffer, '\n');
end while;
if nlPos <> 0 then
if nlPos <> 1 and inFile.readBuffer[pred(nlPos)] = '\r' then
stri := inFile.readBuffer[.. nlPos - 2];
else
stri := inFile.readBuffer[.. pred(nlPos)];
end if;
inFile.readBuffer := inFile.readBuffer[succ(nlPos) ..];
inFile.bufferChar := '\n';
else
stri := inFile.readBuffer;
inFile.readBuffer := "";
inFile.bufferChar := EOF;
end if;
end func;
const func string: getServerCertificate (in file: aFile, in integer: pos) is DYNAMIC;
const func string: getServerCertificate (in tlsFile: aFile, in integer: pos) is
return aFile.parameters.serverCertificates.certList[pos];