erlang SSHA ldap - erlang

Given a LDAP password stored in SHA-1/{SSHA} how would I validate it in erlang.
For example - given the following {SSHA}:
% slappasswd -s myPassword
{SSHA}GEH5kMEQZHYHS95dgr6KmFdg0a4BicBP
%
How would I (in erlang) validate that clear text 'myPassword' matches with the hashed value of '{SSHA}GEH5kMEQZHYHS95dgr6KmFdg0a4BicBP'.

Passwords stored in a directory server are validated using the BIND operation. A properly configured and secured directory server will not allow access to password data; therefore LDAP clients must not be coded expecting that the password data is available, whether encrypted or hashed. LDAP clients must use the BIND operation to validate passwords.

After some help from others I've come up with a routine to do this in Erlang. Following up here to share with others.
First - this link (found in another post) gives functions in other languages doing what I wanted:
http://www.openldap.org/faq/data/cache/347.html
The trick was that the 'ldap {SSHA}' encoding is a salted-SHA1 hash which is also base64 encoded. So - you must decode it, extract the salt and then use that in the re-encoding of the 'clear password' for comparison.
Here is a short Erlang routine which does this:
validatessha(ClearPassword, SshaHash) ->
D64 = base64:decode(lists:nthtail(6, SshaHash)),
{HashedData, Salt} = lists:split(20, binary_to_list(D64)),
NewHash = crypto:sha(list_to_binary(ClearPassword ++ Salt)),
string:equal(binary_to_list(NewHash), HashedData).
Given the data in my original post - here's the output:
67> run:validatessha("myPassword", "{SSHA}GEH5kMEQZHYHS95dgr6KmFdg0a4BicBP").
true
68>
Thanx all.
Mike

My erlang is very rusty, so this isn't very pretty, but maybe it gets my idea along anyway.
run() ->
Password = "myPassword",
HashRaw = os:cmd("slappasswd -s " ++ Password),
Hash1 = lists:nthtail(6, HashRaw),
Hash2 = lists:concat ([integer_to_list(X, 16) || X <- binary_to_list(crypto:sha(Password))]),
string:equal(string:to_lower(Hash1),
string:to_lower(Hash2)).
My idea is that you:
Run the command whose output you are interested in verifying (slappasswd), save the output and trim away the extra decoration preceding the hash.
Run crypto:sha() from the erlang libraries. Take the binary output from this, and convert it to a list of integers, each of which you then convert to a hexadecimal string, which you then concatenate, thereby create Hash2.
Compare the output of your command to the output of crypto:sha()
EDIT: I don't have this command you're using, so I couldn't really try this very thoroughly.. But it works for sha1sum. I hope they are the same!

Related

invalid URL escape "%" when docker push

I set up a registry at docker-registry.elektron.space and when I want to push an image with $ docker push docker-registry.elektron.space/boxbeat-media-server, the upload animation is running in loop for each entity passing from "Pushing" state to "Retrying in X seconds".
After a while I get this error:
failed to parse Location header "https://docker-registry.elektron.space/v2/boxbeat-media-server/blobs/uploads/56244149-c196-439a-85bf-af1121e0b84b%?_state=h1lqY-NljkLbgzTCjd8jxcfdscojPHApblWu-45ISK57Ik5hbWUiOiJib3hiZWF0LW1lZGlhLXNlcnZlciIsIlVVSUQiOiI1NjI0NDE0OS1jMTk2LTQzOWEtODViZi1hZjExMjFlMGI4NGIiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjAtMDMtMDFUMTU6MzI6NTAuMzcxNjc5NTc5WiJ9": parse https://docker-registry.elektron.space/v2/boxbeat-media-server/blobs/uploads/56244149-c196-439a-85bf-af1121e0b84b%?_state=h1lqY-NljkLbgzTCjd8jxcfdscojPHApblWu-45ISK57Ik5hbWUiOiJib3hiZWF0LW1lZGlhLXNlcnZlciIsIlVVSUQiOiI1NjI0NDE0OS1jMTk2LTQzOWEtODViZi1hZjExMjFlMGI4NGIiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjAtMDMtMDFUMTU6MzI6NTAuMzcxNjc5NTc5WiJ9: invalid URL escape "%"
In a readable way:
failed to parse Location header
"https://docker-registry.elektron.space/v2/boxbeat-media-server/blobs/uploads/
56244149-c196-439a-85bf-af1121e0b84b%?_state=
h1lqY-NljkLbgzTCjd8jxcfdscojPHApblWu-45ISK57Ik5hbWUiOiJib3hiZWF0LW1lZGlhLXNlcnZlciIsIlVVSUQiOiI1NjI0NDE0OS1jMTk2LTQzOWEtODViZi1hZjExMjFlMGI4NGIiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjAtMDMtMDFUMTU6MzI6NTAuMzcxNjc5NTc5WiJ9":
parse https://docker-registry.elektron.space/v2/boxbeat-media-server/blobs/uploads/
56244149-c196-439a-85bf-af1121e0b84b%?_state=
h1lqY-NljkLbgzTCjd8jxcfdscojPHApblWu-45ISK57Ik5hbWUiOiJib3hiZWF0LW1lZGlhLXNlcnZlciIsIlVVSUQiOiI1NjI0NDE0OS1jMTk2LTQzOWEtODViZi1hZjExMjFlMGI4NGIiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjAtMDMtMDFUMTU6MzI6NTAuMzcxNjc5NTc5WiJ9:
invalid URL escape "%"
Where does this "%" come from? I thought this could come from zsh then I tried to run it with bash but same result.
Any idea?
The issue is that a % sign is used to initiate an escape sequence in url encoding. You need to escape the % itself.
So in your case you should replace the % with %25 which is the escaped form if it. That way you don't get the error because the parser doesn't think an escape sequence is about to start when it sees the %
... /uploads/56244149-c196-439a-85bf-af1121e0b84b%25 ...
This article can also help to understand things better. Even though its about javascript, the information is applicable much broader.
You can lookup escape sequences on this page.

Trying to decrypt a string using openssl/golang which has been encrypted in rails

I am trying to decrypt a string which has been encrypted in my rails project. This is how I am encrypting the data:
def encrypt_text(text_To_encrypt)
# 0. generate the key using command openssl rand -hex 16 on linux machines
# 1. Read the secret from config
# 2. Read the salt from config
# 3. Encrypt the data
# 4. return the encypted data
# Ref: http://www.monkeyandcrow.com/blog/reading_rails_how_does_message_encryptor_work/
secret = Rails.configuration.miscconfig['encryption_key']
salt = Rails.configuration.miscconfig['encryption_salt']
key = ActiveSupport::KeyGenerator.new(secret).generate_key(salt, 32)
crypt = ActiveSupport::MessageEncryptor.new(key)
encrypted_data = crypt.encrypt_and_sign(text_To_encrypt)
encrypted_data
end
Now the issue is I am not able to decrypt it using openssl. It just shows bad magic number. Once I do that in open ssl, my plan is to decrypt it in golang.
Here is how I tried to descrypt it using openssl:
openssl enc -d -aes-256-cbc -salt -in encrypted.txt -out decrypted.txt -d -pass pass:<the key given in rails> -a
This just shows bad magic number
Trying to decrypt data encrypted in a different system will not work unless you are aware and deal with the many intricate details of how both systems do the cryptography. Although both Rails and the openssl command line tool use the OpenSSL libraries under the hood for their crypto operations, they both use it in their own distinct ways that are not directly interoperable.
If you look close to the two systems, you'll see that for example:
Rails message encryptor not only encrypts the message but also signs it
Rails encryptor uses Marshal to serialize the input data
the openssl enc tool expects the encrypted data in a distinct file format with a Salted__<salt> header (this is why you get the bad magic number message from openssl)
the openssl tool must be properly configured to use the same ciphers as Rails encryptor and key generator, as openssl defaults are different from Rails defaults
the default ciphers configuration changed significantly since Rails 5.2.
With this general info, we can have a look at a a practical example. It is tested in Rails 4.2 but should work equally up to Rails 5.1.
Anatomy of a Rails-encrypted message
Let me start with a slightly amended code that you presented. The only changes there are to preset the password and salt to static values and print a lot of debug info:
def encrypt_text(text_to_encrypt)
password = "password" # the password to derive the key
salt = "saltsalt" # salt must be 8 bytes
key = ActiveSupport::KeyGenerator.new(password).generate_key(salt, 32)
puts "salt (hexa) = #{salt.unpack('H*').first}" # print the saltin HEX
puts "key (hexa) = #{key.unpack('H*').first}" # print the generated key in HEX
crypt = ActiveSupport::MessageEncryptor.new(key)
output = crypt.encrypt_and_sign(text_to_encrypt)
puts "output (base64) = #{output}"
output
end
encrypt_text("secret text")
When you run this, you'll get something like the following output:
salt (hexa) = 73616c7473616c74
key (hexa) = 196827b250431e911310f5dbc82d395782837b7ae56230dce24e497cf07b6518
output (base64) = SGRTUXYxRys1N1haVWNpVWxxWTdCMHlyMk15SnQ0dWFBOCt3Z0djWVdBZz0tLTkrd1hBNWJMVm9HcnptZ3loOG1mNHc9PQ==--80d091e8799776113b2c0efd1bf75b344bf39994
The last line (output of the encrypt_and_sign method) is a combination of two parts separated by -- (see source):
the encrypted message (Base64-encoded) and
the message signature (Base64-encoded).
The signature is not important for encryption so let's take a look in the first part - let's decode it in Rails console:
> Base64.strict_decode64("SGRTUXYxRys1N1haVWNpVWxxWTdCMHlyMk15SnQ0dWFBOCt3Z0djWVdBZz0tLTkrd1hBNWJMVm9HcnptZ3loOG1mNHc9PQ==")
=> "HdSQv1G+57XZUciUlqY7B0yr2MyJt4uaA8+wgGcYWAg=--9+wXA5bLVoGrzmgyh8mf4w=="
You can see that the decoded message again consists of two Base64-encoded parts separated by -- (see source):
the encrypted message itself
the initialization vector used in the encryption
Rails message encryptor uses the aes-256-cbc cipher by default (note that this has changed since Rails 5.2). This cipher needs an initialization vector, which is randomly generated by Rails and must be present in the encrypted output so that we can use it together with the key to decipher the message.
Moreover, Rails does not encrypt the input data as a simple plain text, but rather a serialized version of the data, using the Marshal serializer by default (source). If we decrypted such serialized value with openssl, we would still get a slightly garbled (serialized) version of the initial plain text data. That's why it will be more appropriate to disable serialization while encrypting the data in Rails. This can be done by passing a parameter to the encryption method:
# crypt = ActiveSupport::MessageEncryptor.new(key)
crypt = ActiveSupport::MessageEncryptor.new(key, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
A re-run of the code yields output that is slightly shorter than the previous version, because the encrypted data has not been serialized now:
salt (hexa) = 73616c7473616c74
key (hexa) = 196827b250431e911310f5dbc82d395782837b7ae56230dce24e497cf07b6518
output (base64) = SUlIWFBjSXRUc0JodEMzLzhXckJzUT09LS1oZGtPV1ZRc2I5Wi8zOG01dFNOdVdBPT0=--58bbaf983fd20459062df8b6c59eb470311cbca9
Finally, we must find out some info about the encryption key derivation procedure. The source tells us that the KeyGenerator uses the pbkdf2_hmac_sha1 algorithm with 2**16 = 65536 iterations to derive the key from the password / secret.
Anatomy of an openssl encrypted message
Now, a similar investigation is needed on the openssl side to learn the details of its decryption process. First, if you encrypt anything using the openssl enc tool, you will find out that the output has a distinct format:
Salted__<salt><encrypted_message>
It begins with the Salted__ magic string, then followed by the salt (in hex form) and finally followed by the encrypted data. To be able to decrypt any data using this tool, we must get our encrypted data into the same format.
The openssl tool uses the EVP_BytesToKey (see source) to derive the key by default but can be configured to use the pbkdf2_hmac_sha1 algorithm using the -pbkdf2 and -md sha1 options. The number of iterations can be set using the -iter option.
How to decrypt Rails-encrypted message in openssl
So, finally we have enough information to actually try to decrypt a Rails-encrypted message in openssl.
First we must decode the first part of the Rails-encrypted output again to get the encrypted data and the initialization vector:
> Base64.strict_decode64("SUlIWFBjSXRUc0JodEMzLzhXckJzUT09LS1oZGtPV1ZRc2I5Wi8zOG01dFNOdVdBPT0=")
=> "IIHXPcItTsBhtC3/8WrBsQ==--hdkOWVQsb9Z/38m5tSNuWA=="
Now let's take the IV (the second part) and convert it to a hexa string form, as that is the form that openssl needs:
> Base64.strict_decode64("hdkOWVQsb9Z/38m5tSNuWA==").unpack("H*").first
=> "85d90e59542c6fd67fdfc9b9b5236e58" # the initialization vector in hex form
Now we need to take the Rails-encrypted data and convert it to the format that openssl will recognize, i.e. prepend it with the magic string and salt and Base64-encode it again:
> Base64.strict_encode64("Salted__" + "saltsalt" + Base64.strict_decode64("IIHXPcItTsBhtC3/8WrBsQ=="))
=> "U2FsdGVkX19zYWx0c2FsdCCB1z3CLU7AYbQt//FqwbE=" # encrypted data suitable for openssl
Finally, we can construct the openssl command to decrypt the data:
$ echo "U2FsdGVkX19zYWx0c2FsdCCB1z3CLU7AYbQt//FqwbE=" |
> openssl enc -aes-256-cbc -d -iv 85d90e59542c6fd67fdfc9b9b5236e58 \
> -pass pass:password -pbkdf2 -iter 65536 -md sha1 -a
secret text
And voilá, we successfully decrypted the initial message!
The openssl parameters are as follows:
-aes-256-cbc sets the same cipher as Rails uses for encryption
-d stands for decryption
-iv passes the initialization vector in the hex string form
-pass pass:password sets the password used to derive the encryption key to "password"
-pbkdf2 and -md sha1 set the same key derivation algorithm as is used by Rails (pbkdf2_hmac_sha1)
-iter 65536 sets the same number of iterations for key derivation as was done in Rails
-a allows to work with Base64-encoded encrypted data - no need to handle raw bytes in files
By default openssl reads from STDIN, so we simply pass the encrypted data (in proper format) to openssl using echo.
debugging
In case you hit any problems when decrypting with openssl, it is useful to add the -P parameter to the command line, which outputs debugging info about the cipher / key parameters:
$ echo ... | openssl ... -P
salt=73616C7473616C74
key=196827B250431E911310F5DBC82D395782837B7AE56230DCE24E497CF07B6518
iv =85D90E59542C6FD67FDFC9B9B5236E58
The salt, key, and iv values must correspond to the debugging values printed by the original code in the encrypt_text method printed above. If they are different, you know you are doing something wrong...
Now, I guess you can expect similar problems when trying to decrypt the message in go but I think you have some good pointers now to start.

AES-256-GCM Python cryptography vs Rails OpenSSL

I'm updating a Python script to use cryptography's AESGCM primitive so it can interact with a Rails server running OpenSSL's AES-256-GCM implementation.
To begin, I'm simulating an encryption using identical message/key/nonce to see if both implementations produce the same output.
Python 3
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
data = b"a secret message"
aad = None
key = b'7\x98\xc1\xdf\x7f}\xea5?\\6\x17\tlT\xed\xa2a\x0fn\x87.(\x0c\xe4;*4\xda\x8fY\xc8'
aesgcm = AESGCM(key)
nonce = b'\x8ch\xbe\xfcR\xeee\xc1g\xd6\x80\xda'
ct = aesgcm.encrypt(nonce, data, aad)
ct: b'\xa8\xda\xdd\xdc\xca\xe8X\x84\xdb\x85\xef\xa6\xa6\x95\x00PN\x1e\xe7\xb0\x88\xae\xddc0\x19_\xae\x7f\xfd\x0c.'
Rails
cipher = OpenSSL::Cipher.new('aes-256-gcm').encrypt
data = "a secret message".encode('utf-8')
cipher.key = "7\x98\xc1\xdf\x7f}\xea5?\\6\x17\tlT\xed\xa2a\x0fn\x87.(\x0c\xe4;*4\xda\x8fY\xc8"
cipher.iv = "\x8ch\xbe\xfcR\xeee\xc1g\xd6\x80\xda"
encrypted = cipher.update(data) + cipher.final
encrypted: "\xA8\xDA\xDD\xDC\xCA\xE8X\x84\xDB\x85\xEF\xA6\xA6\x95\x00P"
tag = cipher.auth_tag
tag: "\xB7B\x84h\xDD\xB7y\xCE\x88\xFDI\x9F\xD3\x13\xC51"
From the above examples:
Rails' encrypted is the same as the first part of Python's ct.
Rails' tag is not the same as the second part of Python's ct.
How do I amend one or both of these processes so they produce the same output?
Just found it - the answer lies in how OpenSSL differentially defines data vs auth_data.
The OpenSSL docs are a little confusing in the linked example because they didn't make it clear to me that data refers to the message and auth_data is additional authenticated data.
In my testing, I had mistakenly set auth_data AND data to 'a secret message', which is why data was encrypted consistently but the authenticated data bit at the end was different.
TLDR: data is your message; auth_data is not encrypted and should be set to "" if blank.

Validate URL in Informix 4GL program

In my Informix 4GL program, I have an input field where the user can insert a URL and the feed is later being sent over to the web via a script.
How can I validate the URL at the time of input, to ensure that it's a live link? Can I make a call and see if I get back any errors?
I4GL checking the URL
There is no built-in function to do that (URLs didn't exist when I4GL was invented, amongst other things).
If you can devise a C method to do that, you can arrange to call that method through the C interface. You'll write the method in native C, and then write an I4GL-callable C interface function using the normal rules. When you build the program with I4GL c-code, you'll link the extra C functions too. If you build the program with I4GL-RDS (p-code), you'll need to build a custom runner with the extra function(s) exposed. All of this is standard technique for I4GL.
In general terms, the C interface code you'll need will look vaguely like this:
#include <fglsys.h>
// Standard interface for I4GL-callable C functions
extern int i4gl_validate_url(int nargs);
// Using obsolescent interface functions
int i4gl_validate_url(int nargs)
{
if (nargs != 1)
fgl_fatal(__FILE__, __LINE__, -1318);
char url[4096];
popstring(url, sizeof(url));
int r = validate_url(url); // Your C function
retint(r);
return 1;
}
You can and should check the manuals but that code, using the 'old style' function names, should compile correctly. The code can be called in I4GL like this:
DEFINE url CHAR(256)
DEFINE rc INTEGER
LET url = "http://www.google.com/"
LET rc = i4gl_validate_url(url)
IF rc != 0 THEN
ERROR "Invalid URL"
ELSE
MESSAGE "URL is OK"
END IF
Or along those general lines. Exactly what values you return depends on your decisions about how to return a status from validate_url(). If need so be, you can return multiple values from the interface function (e.g. error number and text of error message). Etc. This is about the simplest possible design for calling some C code to validate a URL from within an I4GL program.
Modern C interface functions
The function names in the interface library were all changed in the mid-00's, though the old names still exist as macros. The old names were:
popstring(char *buffer, int buflen)
retint(int retval)
fgl_fatal(const char *file, int line, int errnum)
You can find the revised documentation at IBM Informix 4GL v7.50.xC3: Publication library in PDF in the 4GL Reference Manual, and you need Appendix C "Using C with IBM Informix 4GL".
The new names start ibm_lib4gl_:
ibm_libi4gl_popMInt()
ibm_libi4gl_popString()
As to the error reporting function, there is one — it exists — but I don't have access to documentation for it any more. It'll be in the fglsys.h header. It takes an error number as one argument; there's the file name and a line number as the other arguments. And it will, presumably, be ibm_lib4gl_… and there'll be probably be Fatal or perhaps fatal (or maybe Err or err) in the rest of the name.
I4GL running a script that checks the URL
Wouldn't it be easier to write a shell script to get the status code? That might work if I can return the status code or any existing results back to the program into a variable? Can I do that?
Quite possibly. If you want the contents of the URL as a string, though, you'll might end up wanting to call C. It is certainly worth thinking about whether calling a shell script from within I4GL is doable. If so, it will be a lot simpler (RUN "script", IIRC, where the literal string would probably be replaced by a built-up string containing the command and the URL). I believe there are file I/O functions in I4GL now, too, so if you can get the script to write a file (trivial), you can read the data from the file without needing custom C. For a long time, you needed custom C to do that.
I just need to validate the URL before storing it into the database. I was thinking about:
#!/bin/bash
read -p "URL to check: " url
if curl --output /dev/null --silent --head --fail "$url"; then
printf '%s\n' "$url exist"
else
printf '%s\n' "$url does not exist"
fi
but I just need the output instead of /dev/null to be into a variable. I believe the only option is to dump the output into a temp file and read from there.
Instead of having I4GL run the code to validate the URL, have I4GL run a script to validate the URL. Use the exit status of the script and dump the output of curl into /dev/null.
FUNCTION check_url(url)
DEFINE url VARCHAR(255)
DEFINE command_line VARCHAR(255)
DEFINE exit_status INTEGER
LET command_line = "check_url ", url
RUN command_line RETURNING exit_status
RETURN exit_status
END FUNCTION {check_url}
Your calling code can analyze exit_status to see whether it worked. A value of 0 indicates success; non-zero indicates a problem of some sort, which can be deemed 'URL does not work'.
Make sure the check_url script (a) exits with status zero on success and non-zero on any sort of failure, and (b) doesn't write anything to standard output (or standard error) by default. The writing to standard error or output will screw up screen layouts, etc, and you do not want that. (You can obviously have options to the script that enable standard output, or you can invoke the script with options to suppress standard output and standard error, or redirect the outputs to /dev/null; however, when used by the I4GL program, it should be silent.)
Your 'script' (check_url) could be as simple as:
#!/bin/bash
exec curl --output /dev/null --silent --head --fail "${1:-http://www.example.com/"
This passes the first argument to curl, or the non-existent example.com URL if no argument is given, and replaces itself with curl, which generates a zero/non-zero exit status as required. You might add 2>/dev/null to the end of the command line to ensure that error messages are not seen. (Note that it will be hell debugging this if anything goes wrong; make sure you've got provision for debugging.)
The exec is a minor optimization; you could omit it with almost no difference in result. (I could devise a scheme that would probably spot the difference; it involves signalling the curl process, though — kill -9 9999 or similar, where the 9999 is the PID of the curl process — and isn't of practical significance.)
Given that the script is just one line of code that invokes another program, it would be possible to embed all that in the I4GL program. However, having an external shell script (or Perl script, or …) has merits of flexibility; you can edit it to log attempts, for example, without changing the I4GL code at all. One more file to distribute, but better flexibility — keep a separate script, even though it could all be embedded in the I4GL.
As Jonathan said "URLs didn't exist when I4GL was invented, amongst other things". What you will find is that the products that have grown to superceed Informix-4gl such as FourJs Genero will cater for new technologies and other things invented after I4GL.
Using FourJs Genero, the code below will do what you are after using the Informix 4gl syntax you are familiar with
IMPORT com
MAIN
-- Should succeed and display 1
DISPLAY validate_url("http://www.google.com")
DISPLAY validate_url("http://www.4js.com/online_documentation/fjs-fgl-manual-html/index.html#c_fgl_nf.html") -- link to some of the features added to I4GL by Genero
-- Should fail and display 0
DISPLAY validate_url("http://www.google.com/testing")
DISPLAY validate_url("http://www.google2.com")
END MAIN
FUNCTION validate_url(url)
DEFINE url STRING
DEFINE req com.HttpRequest
DEFINE resp com.HttpResponse
-- Returns TRUE if http request to a URL returns 200
TRY
LET req = com.HttpRequest.create(url)
CALL req.doRequest()
LET resp = req.getResponse()
IF resp.getStatusCode() = 200 THEN
RETURN TRUE
END IF
-- May want to handle other HTTP status codes
CATCH
-- May want to capture case if not connected to internet etc
END TRY
RETURN FALSE
END FUNCTION

HMAC SHA256 hex digest of a string in Erlang, how?

I am trying to interact with third party real time Web messaging System created and maintained by Pusher.com. Now, i cannot send anything through the API unless i produce an HMAC SHA256 hex digest of my data. A sample source code written in ruby could try to illustrate this:
# Dependencies
# gem install ruby-hmac
#
require 'rubygems'
require 'hmac-sha2'
secret = '7ad3773142a6692b25b8'
string_to_sign = "POST\n/apps/3/channels/test_channel/events\nauth_key=278d425bdf160c739803&auth_timestamp=1272044395&auth_version=1.0&body_md5=7b3d404f5cde4a0b9b8fb4789a0098cb&name=foo"
hmac = HMAC::SHA256.hexdigest(secret, string_to_sign)
puts hmac
# >> 309fc4be20f04e53e011b00744642d3fe66c2c7c5686f35ed6cd2af6f202e445
I checked the erlang crypto Library and i cannot even generate a SHA256 hex digest "directly"
How do i do this whole thing in Erlang ? help....
* UPDATE *
I have found solutions here: sha256 encryption in erlang and they have led me to erlsha2. But still, how do i generate the HMAC of a SHA256 hexdigest output from this module ?
With erlsha2, use the following to get the equivalent of your Ruby code:
1> hmac:hexlify(hmac:hmac256(<<"7ad3773142a6692b25b8">>, <<"POST\n/apps/3/channels/test_channel/events\nauth_key=278d425bdf160c739803&auth_timestamp=1272044395&auth_version=1.0&body_md5=7b3d404f5cde4a0b9b8fb4789a0098cb&name=foo">>)).
"309FC4BE20F04E53E011B00744642D3FE66C2C7C5686F35ED6CD2AF6F202E445"
I just stumbled through this myself and finally managed it just using crypto, so thought I would share. For your usage I think you would want:
:crypto.hmac(:sha256, secret, string_to_sign) |> Base.encode16
The hmac portion should take care of digest + hmac and then piping to encode 16 should provide the hex part. I imagine you probably moved on some time ago, but since I just had the same issue and wanted to try and figure it out in stdlib stuff I thought I would share.
The same project (erlsha2) has a module for this:
https://github.com/vinoski/erlsha2/blob/master/src/hmac.erl
If you're using Elixir, you can use
:crypto.hash(:sha256, [secret, string_to_sign])
|> Base.encode16
|> String.downcase
This is a one-liner (Erlang 24):
[begin if N < 10 -> 48 + N; true -> 87 + N end end ||
<<N:4>> <= crypto:mac(hmac, sha256, Secret1, StringToSign1)].
>>> "309fc4be20f04e53e011b00744642d3fe66c2c7c5686f35ed6cd2af6f202e445"
No need for external libs.

Resources