Congrats on getting it working!

@Rikki Thanks :)

I was trying to write my own lib from beginning based on examples but after some time i resign from that idea (will back to it when i will have some more experience) and right now im trying to customize that one from link which yawniek paste:

https://github.com/yannick/vibe-aws/blob/master/source/vibe/aws/sigv4.d

I removed import from vibe.d library and copy/paste missed functions formEncode and filterURLEncode (BTW: what that "(R)" mean in it? filterURLEncode(R)(ref R dst, ..., ..) ).

Next thing what i did was to replace hmac function to use hmac module from newest library. Now whole code looks like this:

module sigv4;

import std.array;
import std.algorithm;
import std.digest.sha;
import std.range;
import std.stdio;
import std.string;
import std.format;
import std.digest.hmac;


const algorithm = "AWS4-HMAC-SHA256";


void filterURLEncode(R)(ref R dst, string str, string allowed_chars = null, bool form_encoding = false)
{
        while( str.length > 0 ) {
                switch(str[0]) {
                        case ' ':
                                if (form_encoding) {
                                        dst.put('+');
                                        break;
                                }
                                goto default;
                        case 'A': .. case 'Z':
                        case 'a': .. case 'z':
                        case '0': .. case '9':
                        case '-': case '_': case '.': case '~':
                                dst.put(str[0]);
                                break;
                        default:
                                if (allowed_chars.canFind(str[0])) 
dst.put(str[0]);
                                else formattedWrite(dst, "%%%02X", str[0]);
                }
                str = str[1 .. $];
        }
}

string formEncode(string str, string allowed_chars = null)
@safe {
        auto dst = appender!string();
        dst.reserve(str.length);
        filterURLEncode(dst, str, allowed_chars, true);
        return dst.data;
}

struct CanonicalRequest
{
    string method;
    string uri;
    string[string] queryParameters;
    string[string] headers;
    ubyte[] payload;
}

string canonicalQueryString(string[string] queryParameters)
{
    alias encode = formEncode;

    string[string] encoded;
    foreach (p; queryParameters.keys())
    {
        encoded[encode(p)] = encode(queryParameters[p]);
    }
    string[] keys = encoded.keys();
    sort(keys);
    return keys.map!(k => k ~ "=" ~ encoded[k]).join("&");
}

string canonicalHeaders(string[string] headers)
{
    string[string] trimmed;
    foreach (h; headers.keys())
    {
        trimmed[h.toLower().strip()] = headers[h].strip();
    }
    string[] keys = trimmed.keys();
    sort(keys);
    return keys.map!(k => k ~ ":" ~ trimmed[k] ~ "\n").join("");
}

string signedHeaders(string[string] headers)
{
    string[] keys = headers.keys().map!(k => k.toLower()).array();
    sort(keys);
    return keys.join(";");
}

string hash(T)(T payload)
{
    auto hash = sha256Of(payload);
    return hash.toHexString().toLower();
}

string makeCRSigV4(CanonicalRequest r)
{
    auto cr =
        r.method.toUpper() ~ "\n" ~
        (r.uri.empty ? "/" : r.uri) ~ "\n" ~
        canonicalQueryString(r.queryParameters) ~ "\n" ~
        canonicalHeaders(r.headers) ~ "\n" ~
        signedHeaders(r.headers) ~ "\n" ~
        hash(r.payload);

    return hash(cr);
}

unittest {
    string[string] empty;

    auto r = CanonicalRequest(
            "POST",
            "/",
            empty,
["content-type": "application/x-www-form-urlencoded; charset=utf-8",
             "host": "iam.amazonaws.com",
             "x-amz-date": "20110909T233600Z"],
            cast(ubyte[])"Action=ListUsers&Version=2010-05-08");

    auto sig = makeCRSigV4(r);

assert(sig == "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2");
}

struct SignableRequest
{
    string dateString;
    string timeStringUTC;
    string region;
    string service;
    CanonicalRequest canonicalRequest;
}

string signableString(SignableRequest r) {
    return algorithm ~ "\n" ~
        r.dateString ~ "T" ~ r.timeStringUTC ~ "Z\n" ~
r.dateString ~ "/" ~ r.region ~ "/" ~ r.service ~ "/aws4_request\n" ~
        makeCRSigV4(r.canonicalRequest);
}

unittest {
    string[string] empty;

    SignableRequest r;
    r.dateString = "20110909";
    r.timeStringUTC = "233600";
    r.region = "us-east-1";
    r.service = "iam";
    r.canonicalRequest = CanonicalRequest(
            "POST",
            "/",
            empty,
["content-type": "application/x-www-form-urlencoded; charset=utf-8",
             "host": "iam.amazonaws.com",
             "x-amz-date": "20110909T233600Z"],
            cast(ubyte[])"Action=ListUsers&Version=2010-05-08");

    auto sampleString =
        algorithm ~ "\n" ~
        "20110909T233600Z\n" ~
        "20110909/us-east-1/iam/aws4_request\n" ~
"3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2";

    assert(sampleString == signableString(r));
}

ubyte[] array_xor(ubyte[] b1, ubyte[] b2)
{
    assert(b1.length == b2.length);
    ubyte[] ret;
    for (uint i = 0; i < b1.length; i++)
        ret ~= b1[i] ^ b2[i];
    return ret;
}

auto hmac_sha256(string key, string message)
{
    auto hmac = hmac!SHA256(key.representation);
    hmac.put(message.representation);
    return hmac.finish;
}

unittest {
    string key = "key";
string message = "The quick brown fox jumps over the lazy dog";

string mac = hmac_sha256(key, message).toHexString().toLower(); assert(mac == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
}

auto signingKey(string secret, string dateString, string region, string service)
{
    auto kSecret = "AWS4" ~ secret;
    auto kDate = hmac_sha256(kSecret, dateString);
    auto kRegion = hmac_sha256(kDate, region);
    auto kService = hmac_sha256(kRegion, service);
    return hmac_sha256(kService, "aws4_request");
}

unittest {
    string secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
auto signKey = signingKey(secretKey, "20110909", "us-east-1", "iam");

ubyte[] expected = [152, 241, 216, 137, 254, 196, 244, 66, 26, 220, 82, 43, 171, 12, 225, 248, 46, 105, 41, 194, 98, 237, 21, 229, 169, 76, 144, 239, 209, 227, 176, 231 ];
    assert(expected == signKey);
}

alias sign = hmac_sha256;

unittest {
    auto sampleString =
        "AWS4-HMAC-SHA256\n" ~
        "20110909T233600Z\n" ~
        "20110909/us-east-1/iam/aws4_request\n" ~
"3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2";

    auto secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
auto signKey = signingKey(secretKey, "20110909", "us-east-1", "iam");

auto signature = sign(signKey, sampleString).toHexString().toLower(); auto expected = "ced6826de92d2bdeed8f846f0bf508e8559e98e4b0199114b84c54174deb456c";

    assert(signature == expected);
}

/**
 * CredentialScope == date / region / service / aws4_request
 */
string createSignatureHeader(string accessKeyID, string credentialScope, string[string] reqHeaders, ubyte[] signature)
{
return algorithm ~ " Credential=" ~ accessKeyID ~ "/" ~ credentialScope ~ "/aws4_request, SignedHeaders=" ~ signedHeaders(reqHeaders) ~ ", Signature=" ~ signature.toHexString().toLower();
}

string dateFromISOString(string iso)
{
    auto i = iso.indexOf('T');
if (i == -1) throw new Exception("ISO time in wrong format: " ~ iso);
    return iso[0..i];
}

string timeFromISOString(string iso)
{
    auto t = iso.indexOf('T');
    auto z = iso.indexOf('Z');
if (t == -1 || z == -1) throw new Exception("ISO time in wrong format: " ~ iso);
    return iso[t+1..z];
}

unittest {
    assert(dateFromISOString("20110909T1203Z") == "20110909");
}


void main()
{
    auto sampleString =
        "AWS4-HMAC-SHA256\n" ~
        "20110909T233600Z\n" ~
        "20110909/us-east-1/iam/aws4_request\n" ~
"3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2";

    auto secretKey = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
auto signKey = signingKey(secretKey, "20110909", "us-east-1", "iam");

auto signature = sign(signKey, sampleString).toHexString().toLower();

    writeln(signature);
}

When i try to compile it im getting such error:

[holo@ultraxps test]$ dmd -unittest hello.d
hello.d(196): Error: function sigv4.hmac_sha256 (string key, string message) is not callable using argument types (ubyte[32], string)
[holo@ultraxps test]$ dmd hello.d
hello.d(196): Error: function sigv4.hmac_sha256 (string key, string message) is not callable using argument types (ubyte[32], string)
[holo@ultraxps test]$


Line 196 is: "auto kRegion = hmac_sha256(kDate, region);"

I was looking for ubyte[32] variable but i cant find it anywhere. What is strange unit tests are passing (i think, maybe im wrong).

Reply via email to