I thought I would let you's php gurus know that I am starting to come close to cracking the Sha1 algorithm. And surprisingly it is an easy one to crack. So I would recommend switching to something like the whirlpool algorithm.

Recommended Answers

All 44 Replies

I thought I would let you's php gurus know that I am starting to come close to cracking the Sha1 algorithm. And surprisingly it is an easy one to crack. So I would recommend switching to something like the whirlpool algorithm.

Man you are commited to crack that algorithm! Do you want to get into world record? ;)

And whirlpool, what is it?

Man you are commited to crack that algorithm! Do you want to get into world record? ;)

And whirlpool, what is it?

The whirlpool algorithm can be used with the following code:

<?php
echo hash('whirlpool', 'The quick brown fox jumped over the lazy dog.');
?>
//outputs
802dc377bf6dc4f905b90cf2f1ddb39d4958526c3772bce41c03488701630eeede8 51f5ddc195714ea9e35311a513e31c3b616ffce5756bd963e0fdc092b2f87

As you can see it has a longer hash and although I haven't checked the algorithm's core I would assume that it is by far more secure as 8 characters of hex = one number to decode into words.

The whirlpool algorithm can be used with the following code:

<?php
echo hash('whirlpool', 'The quick brown fox jumped over the lazy dog.');
?>
//outputs
802dc377bf6dc4f905b90cf2f1ddb39d4958526c3772bce41c03488701630eeede8 51f5ddc195714ea9e35311a513e31c3b616ffce5756bd963e0fdc092b2f87

As you can see it has a longer hash and although I haven't checked the algorithm's core I would assume that it is by far more secure as 8 characters of hex = one number to decode into words.

So you just use

hash('whirlpools', 'some text to encode');

So you just use

hash('whirlpools', 'some text to encode');

Except drop the s.

hash('whirlpool', 'some text to encode');

I thought I would let you's php gurus know that I am starting to come close to cracking the Sha1 algorithm. And surprisingly it is an easy one to crack. So I would recommend switching to something like the whirlpool algorithm.

Are you serious? Are you willing to share with us how you're cracking it? It would be quite amazing if you did.

Are you serious? Are you willing to share with us how you're cracking it? It would be quite amazing if you did.

Yes, tomorrow I will have an update where I should have a data array cracked which will lead to the original data. Btw I'm using c++ and I will post some code in 24 hours. If I don't just remind me in 24 hours and I will post.

Do you think it is wise to post that in the public domain so that every nut and his brother can run a muck on the internet?

Do you think it is wise to post that in the public domain so that every nut and his brother can run a muck on the internet?

What do you mean by "run a muck"? I thought it would be wise.

What do you mean by "run a muck"? I thought it would be wise.

Maybe you should think again.
Maybe it's ok to do as a personal intellectual exercise, but what's the point of compromising sha1 for everybody?
I don't get it.

Maybe you should think again.
Maybe it's ok to do as a personal intellectual exercise, but what's the point of compromising sha1 for everybody?
I don't get it.

For better security practices to be taken into place. IMO any hash function can be cracked and I am going to prove that by going to selected algorithms and cracking them. If you truly want a secure hash then you would need to use something like the following:

function truehash($hashzzz) {
return hash('sha1',hash('whirlpool',$hashzzz));
}

A function like that will never be cracked unless both algorithms are cracked with a third algorithm written. So pritty much the reason why I am revealing the weaknesses in SHA1 is to prove that no hash is truly secure. I heard some officials in America have already cracked sha1 but is closed source. As for my source at digital-ethers request is below. I have managed to store both the previous temp variable value and the core data of the word variable/subarray into a single variable. This variable changes on each loop round. The next step is to separate rotateleft(a,5) from word[m] which are stored in val. After that it is possible to gain access to the word array which will lead to the next set of equations.

//#include<stdio.h>   
#include<string.h>   
#include<malloc.h>   
#include<math.h>   
#include<stdlib.h>   
#include<iostream>

#include <sstream>

#define rotateleft(x,n) ((x<<n) | (x>>(32-n)))   
#define rotateright(x,n) ((x>>n) | (x<<(32-n)))   

#define rotateleftrev(x,n) ((x<<n) | (x>>((32-n)+n)))   
    
void SHA1(unsigned char * str1)   
{   
	int i;
    unsigned long int h0,h1,h2,h3,h4,a,b,c,d,e,f,k,temp;   
    h0 = 0x67452301;   
    h1 = 0xEFCDAB89;   
    h2 = 0x98BADCFE;   
    h3 = 0x10325476;   
    h4 = 0xC3D2E1F0;   
    
    unsigned char * str;   
    str = (unsigned char *)malloc(strlen((const char *)str1)+100);   
    strcpy((char *)str,(const char *)str1);   
    
    int current_length = strlen((const char *)str);   
    int original_length = current_length;   
    str[current_length] = 0x80;   
    str[current_length + 1] = '\0';   
    
    char ic = str[current_length];   
    current_length++;   
    
    int ib = current_length % 64;   
    if(ib<56)   
        ib = 56-ib;   
    else   
        ib = 120 - ib;   
    
    for(i=0;i < ib;i++)   
    {   
        str[current_length]=0x00;   
        current_length++;   
    }   
    str[current_length + 1]='\0';   
    
    for(i=0;i<6;i++)   
    {   
        str[current_length]=0x0;   
        current_length++;   
    }   
    str[current_length] = (original_length * 8) / 0x100 ;   
    current_length++;   
    str[current_length] = (original_length * 8) % 0x100;   
    current_length++;   
    str[current_length+i]='\0';   
    
    int number_of_chunks = current_length/64;   
    unsigned long int word[80];   
    for(i=0;i<number_of_chunks;i++)   
    {   
        for(int j=0;j<16;j++)   
        {   
            word[j] = str[i*64 + j*4 + 0] * 0x1000000 + str[i*64 + j*4 + 1] * 0x10000 + str[i*64 + j*4 + 2] * 0x100 + str[i*64 + j*4 + 3];   
        }   
        for(int j=16;j<80;j++)   
        {   
            word[j] = rotateleft((word[j-3] ^ word[j-8] ^ word[j-14] ^ word[j-16]),1);   
        }   
    
        a = h0;   
        b = h1;   
        c = h2;   
        d = h3;   
        e = h4;   
    
        for(int m=0;m<80;m++)   
        {   
            if(m<=19)   
            {   
                f = (b & c) | ((~b) & d);   
                k = 0x5A827999;   
            }   
            else if(m<=39)   
            {   
                f = b ^ c ^ d;   
                k = 0x6ED9EBA1;   
            }   
            else if(m<=59)   
            {   
                f = (b & c) | (b & d) | (c & d);   
                k = 0x8F1BBCDC;   
            }   
            else   
            {   
                f = b ^ c ^ d;   
                k = 0xCA62C1D6;    
            }   
			//std::cout << word[m] << std::endl;
            temp = (rotateleft(a,5) + f + e + k + word[m]) & 0xFFFFFFFF;   
            if (m==79) {
			std::cout << "+++ " << a << std::endl;
			std::cout << "++ " << rotateleft(a,5) << std::endl;
			}
			e = d;   
            d = c;
            c = rotateleft(b,30);   //std::cout << "++ " << b << std::endl;
            b = a;
			a = temp;   
    
        }   
		//std::cout << "--++ " << c << std::endl;
		//std::cout << b << std::endl;
		//std::cout << c << std::endl;
		//std::cout << h3 << "+" << d << "=" << (h3+d) << std::endl;
		//printf("%x",(h3+d));
		std::cout << std::endl;
		//std::cout << e << std::endl;
        h0 = h0 + a;   
        h1 = h1 + b;   
        h2 = h2 + c;   
        h3 = h3 + d;   
        h4 = h4 + e;   
    
    }   
    
	printf("Hash: %x %x %x %x %x",h0, h1, h2, h3, h4);
	std::cout << std::endl << "Hash: " << h0 << " " << h1 << " " << h2 << " " << h3 << " " << h4 << std::endl;
	
}   
//----------------------------------------------
// edit below
//----------------------------------------------
unsigned long int bin2dec(std::string bin)
{
  unsigned long int result=0;
  for(unsigned int i=0;i<bin.length();i++)
  {
    //if((bin[i]!='0')&&(bin[i]!='1'))
    //  return -1;
    result=result*2+(bin[i]-'0');
    //if(result<=0) return -1;
  }
  return result;
}

std::string *parts8(std::string originalhash) {
	int arr_pos=0;
	std::string chrset="";
	std::string *parts;
	parts = new std::string[5];
	for (int i=0; i<40; i++) {
		double tmp=(i+1)/8;
		arr_pos=floor(tmp)-1;
		tmp=((i+1.0)/8.0)-1.0;

		std::string str;
		str=originalhash.at(i);
		chrset=chrset.append(str.c_str());
		if (arr_pos==tmp) {
			parts[arr_pos]=chrset;
			chrset="";
			}
		}
	
	return parts;
}
char xtod(char c) {
 if (c>='0' && c<='9') return c-'0';
 if (c>='A' && c<='F') return c-'A'+10;
 if (c>='a' && c<='f') return c-'a'+10;
 return c=0;        // not Hex digit
}
/*long hex2int(const std::string& hexStr) {
  
  char *offset;

  if (hexStr.length( ) > 2) {
    if (hexStr[0] == '0' && hexStr[1] == 'x') {
      return strtol(hexStr.c_str( ), &offset, 0);
    }else{
		std::string str="0x";
		str.append(hexStr);
		return strtol(str.c_str( ), &offset, 0);
    }
  }
  
  }*/
typedef __int64 int64;
 
int64 hexToInt64(const std::string hexStr){
	std::stringstream strm;
	strm << std::hex << hexStr;
	int64 value = 0;
	if(!(strm >> value)) throw std::exception("Conversion to int64 failed!\n");
	return value;
}
unsigned long int hex2int(const std::string hexString) {
unsigned long int v;
v=hexToInt64(hexString);
return v;
}

std::string dehash_SHA1 (std::string originalhash) {
	if (originalhash.length()!=40) {
		return "^Error: Invalid hash";
	} else {
		std::string *arr;
		arr=parts8(originalhash);
		
		unsigned long int *parts;
		parts = new unsigned long int[5];
	parts[0]=hex2int(arr[0]);
	parts[1]=hex2int(arr[1]);
	parts[2]=hex2int(arr[2]);
	parts[3]=hex2int(arr[3]);
	//std::cout << "Num=" << parts[3] << std::endl;
	parts[4]=hex2int(arr[4]);

	/*std::cout << parts[0] << std::endl;
	std::cout << parts[1] << std::endl;
	std::cout << parts[2] << std::endl;
	std::cout << parts[3] << std::endl;
	std::cout << parts[4] << std::endl;*/
	unsigned long int h0,h1,h2,h3,h4, a,b,c,d,e,k,f, ulong,temp,val;
	unsigned int zplus;
	zplus=500;
	unsigned long int worddata[80][500];
    unsigned long int worddata2[80][16];
	h0 = 0x67452301;
    h1 = 0xEFCDAB89;
    h2 = 0x98BADCFE;
    h3 = 0x10325476;
    h4 = 0xC3D2E1F0;
	ulong = 4294967295;
	ulong = 18446744073709551615;
	//std::cout << std::endl << parts[3] << "-" << h3 << "=" << (parts[3]-h3) << std::endl;
	
	// --- loop beginning --- //
	a=parts[0]-h0; parts[0]=a;
	b=parts[1]-h1; parts[1]=b;
	c=parts[2]-h2; parts[2]=c;
	d=parts[3]-h3; parts[3]=d;
	e=parts[4]-h4; parts[4]=e;
	
	for (int m=79; m>=0; m--) {
		if(m<=19)   
            {   
                f = (b & c) | ((~b) & d);   
                k = 0x5A827999;   
            }   
            else if(m<=39)   
            {   
                f = b ^ c ^ d;   
                k = 0x6ED9EBA1;   
            }   
            else if(m<=59)   
            {   
                f = (b & c) | (b & d) | (c & d);   
                k = 0x8F1BBCDC;   
            }   
            else   
            {   
                f = b ^ c ^ d;   
                k = 0xCA62C1D6;    
            } 
		//c = rotateright();
		//std::cout << "++" << b << std::endl;
		//std::cout << "++" << b << std::endl;
		//std::cout << "++" << c << std::endl;
		//temp is smaller than var a;
			/*temp = (rotateleft(a,5) + f + e + k + word[m]) & 0xFFFFFFFF;   
            e = d;   
            d = c;
            c = rotateleft(b,30);  //// std::cout << "++ " << b << std::endl;
            b = a;   
            a = temp;*/
			
			temp = a;
			val=0;
			while (true) {
				if ((( f + e + k + val) & 0xFFFFFFFF)==temp) {
				break;
				} else {
				val++;
				if ((( f + e + k + val + 1000000) & 0xFFFFFFFF)<temp) {
					val+=1000000;
					}
				}
			}
			unsigned int zz=1;
			for (unsigned int z=0;z<zplus;z++) {
				if (rotateleft(zz,5)<val) {
					if (rotateleft(zz,5)>0) {
						worddata[m][z]=rotateleft(zz,5);
						std::cout << worddata[m][z] << std::endl;
					}
				}
				zz++;
			}
			std::cout << std::endl;
			/*
			val = rotateleft(a,5) + word[m] The current value of val
			worddata[x] contains the possible numbers of word[m].
			worddata2 needs to be populated by going through the array worddata
			and changing the last for binary digits to 1010, 1101, 1011 etc.

			Get number smaller than the val variable and it's binary value ends in 00000
			That is ends in 5 zeros for it's binary value.
			*/
            a = b;
			if (m==78) {
			std::cout << a << std::endl;
			}
            b = rotateright(c,30);
			c = d;
			d = e;
		}
	//std::cout << d << std::endl;
	//std::cout << std::endl << parts[0] << std::endl;
	//std::cout << parts[1] << std::endl;
	//std::cout << parts[2] << std::endl;
	//std::cout << parts[3] << "--" << 0x10325476 << std::endl;
	//std::cout << parts[4] << std::endl;

	return "";
	}
}

void main()   
{   
	std::string tmp;
	coda:
	system("CLS");
    SHA1((unsigned char *)"The quick brown fox jumps over the lazy dog");
	
	//above and below are same hash

	tmp=dehash_SHA1("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
	std::cout << tmp.c_str() << std::endl;

	std::cout << std::endl;
	std::cout << bin2dec("1101") << std::endl;

	system("PAUSE");
	goto coda;
}

goto coda;

Infamous phrase gOtO ;)

Infamous phrase gOtO ;)

I don't see how that stops without ctrl-c. You are trying to match the phrase to it's hash code? How do you know when you got it?
My c++ is a bit rusty, but I guess you started in c and decided c++ was easier to deal with?. I'm not going to pretend that I know anything about code cracking. I don't understand what you are displaying in main() . I think it will look like one of those cracking devices they use in the movies where they attach the thing to an electronic safe and it rotates through a number sequences till the safe pops open?

I dunno either.
It burned a few minutes of CPU time, and printed out 0 to 16000 (in steps of 32) a total of 80 times.

But even uncommenting some extra print statements around line 320 (which would appear to be the result) didn't leave me any the wiser.

Member Avatar for diafol

This is way beyond me, but looks really impressive. Does it work? If so, is SHA1 doomed?

I dunno either.
It burned a few minutes of CPU time, and printed out 0 to 16000 (in steps of 32) a total of 80 times.

But even uncommenting some extra print statements around line 320 (which would appear to be the result) didn't leave me any the wiser.

What you saw was the raw diagnostics checking the comparisons for the dehash vs hash. At the moment I managed to get a match on the first loop for two variables added and and am working on separating those two variables to allow all loop rounds work and to get the core data. So at the moment I haven't completed it but am coming very close.

This is way beyond me, but looks really impressive. Does it work? If so, is SHA1 doomed?

Yes sha1 is doomed and it will be soon before it is cracked.

What you saw was the raw diagnostics checking the comparisons for the dehash vs hash. At the moment I managed to get a match on the first loop for two variables added and and am working on separating those two variables to allow all loop rounds work and to get the core data. So at the moment I haven't completed it but am coming very close.

yes sha1 is doomed and it will be soon before it is cracked.

Doesn't the algorithm encode based on the ENTIRE input? I would guess that it makes a hex representation of the string probably from th ascii value, then applies the encryption. So, the string randomizes the resultant hash.
Making a two variable match could even be a random result. Do you get matches with ANY OTHER input?

Logically, I don't see how you think you can crack sha1, one variable at time.
What's the science behind your approach?

What's the science behind your approach?

Basically reversing the math and reversing the algorithm to get the reverse result. I know with any good algorithm that should be impossible but with SHA1 I have been having great success. IMO SHA1 is Insecure Hash Algorithm because it has so many security floors.

But hashes are ONE WAY FUNCTIONS!

Trivial example - the hash function is strlen().
That means the hash of "SEND ONE DOLLAR" and "SEND 1M DOLLARS" are the same. But there is zero information in the hash result to tell you which one it was.

The hash of an entire book (say war and peace) would still only be 40 characters. Are you seriously thinking you could recover the entire book? Who needs ZIP, when we've got your reversible hash.

Now SHA1 is more secure, because there is a lot less chance of getting the same result from any given two messages.
It is also more secure, because it is hard to arrange for a particular message to have a particular SHA1 result.

You can't get back the original text just by "reversing" the process, even if you could.

Read Bruce Schneier's post from 5 YEARS AGO.
If you've only managed a couple of rounds, are you doing anything which isn't just brute force (or something slightly less expensive).

But hashes are ONE WAY FUNCTIONS!

Trivial example - the hash function is strlen().
That means the hash of "SEND ONE DOLLAR" and "SEND 1M DOLLARS" are the same. But there is zero information in the hash result to tell you which one it was.

The hash of an entire book (say war and peace) would still only be 40 characters. Are you seriously thinking you could recover the entire book? Who needs ZIP, when we've got your reversible hash.

Now SHA1 is more secure, because there is a lot less chance of getting the same result from any given two messages.
It is also more secure, because it is hard to arrange for a particular message to have a particular SHA1 result.

You can't get back the original text just by "reversing" the process, even if you could.

Read Bruce Schneier's post from 5 YEARS AGO.
If you've only managed a couple of rounds, are you doing anything which isn't just brute force (or something slightly less expensive).

If I get this algorithm right, you can place in a hash then the computer will spit out every combination that matches that hash be using a reverse algorithm. Of course I would need to limit it to spit out 1000 dehashed results but as for now I have bumped into a slight problem. The below demonstrates it on the encryption side.

for (i=0;i<80;i++) {
a=b;
b=c;
c=c+1;
}

Now when you reverse that it looks like

for (i=79;i>=0;i--) {
c=c+1;
c=b;
b=a;
a=(unknown)
}

So as for the answer since how in the sha1 algorithm I have the first value of a and the last value of a (or in the algorithm is represented by e) then theoretically it should be possible to fill in the gap. If I do pass by this problem and didn't go any further I would have made dehashing 80 times faster by brute force.

> you can place in a hash then the computer will spit out every combination that matches that hash be using a reverse algorithm
Huh?

So you intend to output
- every message which is 1 byte long (that matches the hash)
- every message which is 2 bytes long (that matches the hash)
- every message which is 3 bytes long (that matches the hash)
- every message which is 4 bytes long (that matches the hash)
- etc ad nauseum until you disk fills up?

Member Avatar for diafol

I apologise in advance for my naivety, but isn't the fact that you get one result enough to hack an unsalted password field in a login form?
If you get the hash (e.g. somehow from the db), you could just use the first result from the algorithm to gain access?

I apologise in advance for my naivety, but isn't the fact that you get one result enough to hack an unsalted password field in a login form?
If you get the hash (e.g. somehow from the db), you could just use the first result from the algorithm to gain access?

You are not alone lost in these geeky way of talking. I was hinking something like that and...I'm lost :)

I apologise in advance for my naivety, but isn't the fact that you get one result enough to hack an unsalted password field in a login form?
If you get the hash (e.g. somehow from the db), you could just use the first result from the algorithm to gain access?

Don't feel bad, I know nothing of cryptology, but I Do know some mathematics and this approach does not seem to be the way to do "reverse engineering" of an algorithm to me.

Having one or two character matches in a specific string/hash set could even be a random event in itself.

Heck, people have been trying to make successful de-compilers for years with limited success, and the compilation algorithm is known!
In the case of the hash code, you don't have any knowledge of the starting point at all.
There are math oriented ways to probe the inner secrets of things, but this program is not the way to go. Even with the right science, it's not easy to get all the way in.
If it were that easy the banking system would have collapsed due to internet theft long ago. The thieves on Wall st. are bad enough!

people have been trying to make successful de-compilers for years with limited success, and the compilation algorithm is known!

Although I'm ignorant about cryptology that sounds very logical.
But how comes Even 100 characters long produce 40 Hash character. It kinda abracadabra doesn't it?

The sum is a lossy algorithm. Bytes are rotated and shifted and split and combined. Bits are overwritten and dropped. But then, that's what sums and hashes are supposed to do.

The practical intent is that these sums are one-to-one mappings. In theory, more than one source can map to a single sum. If it's just the sum that must match, then it doesn't matter which source is used to generate the sum. As a real life example, it doesn't matter what you enter for a password; what matters is that what you enter passes through the one-way algorithm to be transformed into the stored value.

This is one reason static passwords aren't all that secure, and it's pretty much the same reason why 'captcha' images don't work too well to keep spambots out of discussion fora. "Hey, spammer! Here's the password. What is it?" Well, duh!

FWIW, challenge/response systems *do* work well to keep bots out, at least those that require a human to answer the challenge. Dynamic passwords (challenges), like the old SecurID cards, work well to keep miscreants at bay. But I digress.

If you can find two source strings that have the same SHA1 sum, you will have reduced the usefulness of SHA1, just as the usefulness of MD5 sums were reduced some years ago in the same way. But then, anyone wanting stronger security will use sha[224|256|384|512]sum

But hashes are ONE WAY FUNCTIONS!

Trivial example - the hash function is strlen().
That means the hash of "SEND ONE DOLLAR" and "SEND 1M DOLLARS" are the same. But there is zero information in the hash result to tell you which one it was.

The hash of an entire book (say war and peace) would still only be 40 characters. Are you seriously thinking you could recover the entire book? Who needs ZIP, when we've got your reversible hash.

Now SHA1 is more secure, because there is a lot less chance of getting the same result from any given two messages.
It is also more secure, because it is hard to arrange for a particular message to have a particular SHA1 result.

You can't get back the original text just by "reversing" the process, even if you could.

Read Bruce Schneier's post from 5 YEARS AGO.
If you've only managed a couple of rounds, are you doing anything which isn't just brute force (or something slightly less expensive).

Salem, I agree with you but your implied definition of one way functions is misleading. One way functions do not have anything to do with the loss of information. That has to do with the fixed size of the message digest which is for practicality reasons.

One way functions are irreversible functions, in terms of feasibility of computing a matching input from an output. Thus they are used to generate the one to one map from an input to an output, "randomly" within the space taken up by all possible digests.

If you used any lossy algorithm, it wouldn't make it a one way function, since it is reversible in terms of feasibility of computing a matching input, from a given output.

It is trivial to generate a string that is 5 chars long for instance. So strlen() is not a one way function.

The sum is a lossy algorithm. Bytes are rotated and shifted and split and combined. Bits are overwritten and dropped. But then, that's what sums and hashes are supposed to do.

The practical intent is that these sums are one-to-one mappings. In theory, more than one source can map to a single sum. If it's just the sum that must match, then it doesn't matter which source is used to generate the sum. As a real life example, it doesn't matter what you enter for a password; what matters is that what you enter passes through the one-way algorithm to be transformed into the stored value.

This is one reason static passwords aren't all that secure, and it's pretty much the same reason why 'captcha' images don't work too well to keep spambots out of discussion fora. "Hey, spammer! Here's the password. What is it?" Well, duh!

FWIW, challenge/response systems *do* work well to keep bots out, at least those that require a human to answer the challenge. Dynamic passwords (challenges), like the old SecurID cards, work well to keep miscreants at bay. But I digress.

If you can find two source strings that have the same SHA1 sum, you will have reduced the usefulness of SHA1, just as the usefulness of MD5 sums were reduced some years ago in the same way. But then, anyone wanting stronger security will use sha[224|256|384|512]sum

Sha512. That's a great algorithm to crack. But sha1 first.
As for about how bytes are lost and removed, I have created algorithms that generates those bytes from existing data. That is a very simple and effective concept. Got me very far until I came across this one variable which I will need to write a big algorithm for. So when you think about it I am like Macgyver and Neo combined. I see everything as a matrix and use existing items to solve the hardest problems. With that concept in mind I should be able to crack this layer of sha1 and possibly the entire algorithm.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.