firefly-compiler 0.4.35 → 0.4.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/Compiler.ff +1 -1
- package/core/Crypto.ff +95 -0
- package/core/NodeSystem.ff +3 -0
- package/crypto/SubtleCrypto.ff +149 -0
- package/experimental/s3/S3.ff +32 -65
- package/experimental/s3/S3TestAuthorizationHeader.ff +39 -0
- package/experimental/s3/S3TestPut.ff +16 -0
- package/output/js/ff/compiler/Builder.mjs +1 -1
- package/output/js/ff/compiler/Compiler.mjs +2 -2
- package/output/js/ff/compiler/Dependencies.mjs +1 -1
- package/output/js/ff/compiler/DependencyLock.mjs +1 -1
- package/output/js/ff/compiler/Deriver.mjs +1 -1
- package/output/js/ff/compiler/Dictionaries.mjs +1 -1
- package/output/js/ff/compiler/Environment.mjs +1 -1
- package/output/js/ff/compiler/Inference.mjs +1 -1
- package/output/js/ff/compiler/JsEmitter.mjs +1 -1
- package/output/js/ff/compiler/JsImporter.mjs +1 -1
- package/output/js/ff/compiler/LspHook.mjs +1 -1
- package/output/js/ff/compiler/Main.mjs +1 -1
- package/output/js/ff/compiler/ModuleCache.mjs +1 -1
- package/output/js/ff/compiler/Parser.mjs +1 -1
- package/output/js/ff/compiler/Patterns.mjs +1 -1
- package/output/js/ff/compiler/Resolver.mjs +1 -1
- package/output/js/ff/compiler/Substitution.mjs +1 -1
- package/output/js/ff/compiler/Syntax.mjs +1 -1
- package/output/js/ff/compiler/Token.mjs +1 -1
- package/output/js/ff/compiler/Tokenizer.mjs +1 -1
- package/output/js/ff/compiler/Unification.mjs +1 -1
- package/output/js/ff/compiler/Wildcards.mjs +1 -1
- package/output/js/ff/compiler/Workspace.mjs +1 -1
- package/output/js/ff/core/Any.mjs +1 -1
- package/output/js/ff/core/Array.mjs +1 -1
- package/output/js/ff/core/AssetSystem.mjs +1 -1
- package/output/js/ff/core/Atomic.mjs +1 -1
- package/output/js/ff/core/Bool.mjs +1 -1
- package/output/js/ff/core/BrowserSystem.mjs +1 -1
- package/output/js/ff/core/Buffer.mjs +1 -1
- package/output/js/ff/core/BuildSystem.mjs +1 -1
- package/output/js/ff/core/Channel.mjs +1 -1
- package/output/js/ff/core/Char.mjs +1 -1
- package/output/js/ff/core/Core.mjs +1 -1
- package/output/js/ff/core/Crypto.mjs +275 -0
- package/output/js/ff/core/Duration.mjs +1 -1
- package/output/js/ff/core/Equal.mjs +1 -1
- package/output/js/ff/core/Error.mjs +1 -1
- package/output/js/ff/core/FileHandle.mjs +1 -1
- package/output/js/ff/core/Float.mjs +1 -1
- package/output/js/ff/core/HttpClient.mjs +1 -1
- package/output/js/ff/core/Instant.mjs +1 -1
- package/output/js/ff/core/Int.mjs +1 -1
- package/output/js/ff/core/IntMap.mjs +1 -1
- package/output/js/ff/core/JsSystem.mjs +1 -1
- package/output/js/ff/core/JsValue.mjs +1 -1
- package/output/js/ff/core/Json.mjs +1 -1
- package/output/js/ff/core/List.mjs +1 -1
- package/output/js/ff/core/Lock.mjs +1 -1
- package/output/js/ff/core/Log.mjs +1 -1
- package/output/js/ff/core/Map.mjs +1 -1
- package/output/js/ff/core/NodeSystem.mjs +9 -1
- package/output/js/ff/core/Nothing.mjs +1 -1
- package/output/js/ff/core/Option.mjs +1 -1
- package/output/js/ff/core/Ordering.mjs +1 -1
- package/output/js/ff/core/Pair.mjs +1 -1
- package/output/js/ff/core/Path.mjs +1 -1
- package/output/js/ff/core/Random.mjs +1 -1
- package/output/js/ff/core/RbMap.mjs +1 -1
- package/output/js/ff/core/Serializable.mjs +1 -1
- package/output/js/ff/core/Set.mjs +1 -1
- package/output/js/ff/core/Show.mjs +1 -1
- package/output/js/ff/core/SourceLocation.mjs +1 -1
- package/output/js/ff/core/Stream.mjs +1 -1
- package/output/js/ff/core/String.mjs +1 -1
- package/output/js/ff/core/StringMap.mjs +1 -1
- package/output/js/ff/core/Task.mjs +1 -1
- package/output/js/ff/core/Try.mjs +1 -1
- package/output/js/ff/core/Unit.mjs +1 -1
- package/package.json +1 -1
- package/vscode/package.json +1 -1
- package/core/Digest.ff +0 -51
- package/output/js/ff/core/Digest.mjs +0 -190
package/compiler/Compiler.ff
CHANGED
package/core/Crypto.ff
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
capability Crypto {}
|
|
2
|
+
|
|
3
|
+
extend self: Crypto {
|
|
4
|
+
|
|
5
|
+
randomUuid(): String
|
|
6
|
+
target js async """
|
|
7
|
+
return self_.randomUUID();
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
randomBuffer(size: Int): Buffer {
|
|
11
|
+
let buffer = Buffer.new(size)
|
|
12
|
+
self.randomizeBuffer(buffer)
|
|
13
|
+
buffer
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
randomizeBuffer(buffer: Buffer): Unit
|
|
17
|
+
target js async """
|
|
18
|
+
self_.getRandomValues(new Uint8Array(buffer_.buffer, buffer_.byteOffset, buffer_.byteLength));
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
hmacSha256(key: Buffer, buffer: Buffer): Buffer
|
|
22
|
+
target js async """
|
|
23
|
+
const cryptoKey = await self_.subtle.importKey(
|
|
24
|
+
'raw',
|
|
25
|
+
key_,
|
|
26
|
+
{name: 'HMAC', hash: {name: 'SHA-256'}},
|
|
27
|
+
false,
|
|
28
|
+
['sign']
|
|
29
|
+
);
|
|
30
|
+
const signature = await self_.subtle.sign(
|
|
31
|
+
'HMAC',
|
|
32
|
+
cryptoKey,
|
|
33
|
+
buffer_
|
|
34
|
+
);
|
|
35
|
+
return new DataView(signature);
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
sha256(buffer: Buffer): Buffer
|
|
39
|
+
target js async """
|
|
40
|
+
let hash = await self_.subtle.digest('SHA-256', buffer_);
|
|
41
|
+
return new DataView(hash);
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
makePasswordHash(password: String, iterations: Int = 100000): String {
|
|
45
|
+
let salt = self.randomBuffer(16)
|
|
46
|
+
let hash = internalMakePasswordHash(salt, password.toBuffer(), iterations)
|
|
47
|
+
"PSG_" + iterations + "_" + salt.toHex() + "_" + hash.toHex()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
checkPasswordHash(password: String, passwordHash: String): Bool {
|
|
51
|
+
passwordHash.split('_').{
|
|
52
|
+
| ["PSG", iterationsText, saltText, hashText] {iterationsText.getInt() | Some(iterations)} =>
|
|
53
|
+
let computedHash = internalMakePasswordHash(Buffer.fromHex(saltText), password.toBuffer(), iterations)
|
|
54
|
+
let hash = Buffer.fromHex(hashText)
|
|
55
|
+
self.constantTimeEquals(computedHash, hash)
|
|
56
|
+
| _ => False
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
constantTimeEquals(buffer1: Buffer, buffer2: Buffer): Bool {
|
|
61
|
+
if(buffer1.size() != buffer2.size()) {False} else:
|
|
62
|
+
mutable v = 0
|
|
63
|
+
mutable i = 0
|
|
64
|
+
while {i < buffer1.size()} {
|
|
65
|
+
v = v.bitOr(buffer1.grabUint8(i).bitXor(buffer2.grabUint8(i)))
|
|
66
|
+
}
|
|
67
|
+
v == 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
internalMakePasswordHash(salt: Buffer, password: Buffer, iterations: Int): Buffer
|
|
73
|
+
target js async """
|
|
74
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
75
|
+
'raw',
|
|
76
|
+
password_,
|
|
77
|
+
{name: 'PBKDF2'},
|
|
78
|
+
false,
|
|
79
|
+
['deriveKey']
|
|
80
|
+
);
|
|
81
|
+
const derivedKey = await crypto.subtle.deriveKey(
|
|
82
|
+
{
|
|
83
|
+
name: 'PBKDF2',
|
|
84
|
+
salt: salt_,
|
|
85
|
+
iterations: iterations_,
|
|
86
|
+
hash: 'SHA-256'
|
|
87
|
+
},
|
|
88
|
+
keyMaterial,
|
|
89
|
+
{name: 'AES-GCM', length: 256},
|
|
90
|
+
true,
|
|
91
|
+
['encrypt', 'decrypt']
|
|
92
|
+
);
|
|
93
|
+
const hashBuffer = await crypto.subtle.exportKey('raw', derivedKey);
|
|
94
|
+
return new DataView(hashBuffer);
|
|
95
|
+
"""
|
package/core/NodeSystem.ff
CHANGED
|
@@ -45,6 +45,9 @@ extend self: NodeSystem {
|
|
|
45
45
|
mainTask(): Task
|
|
46
46
|
target js async "return self_.task_"
|
|
47
47
|
|
|
48
|
+
crypto()
|
|
49
|
+
target js async "return (typeof globalThis !== 'undefined' ? globalThis : window).crypto"
|
|
50
|
+
|
|
48
51
|
js(): JsSystem
|
|
49
52
|
target js async "return typeof globalThis !== 'undefined' ? globalThis : window"
|
|
50
53
|
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import Crypto from ff:core
|
|
2
|
+
|
|
3
|
+
capability SubtleCrypto(crypto: Crypto)
|
|
4
|
+
|
|
5
|
+
data CryptoKey {}
|
|
6
|
+
|
|
7
|
+
data CryptoKeyUsages(
|
|
8
|
+
encrypt: Bool = False
|
|
9
|
+
decrypt: Bool = False
|
|
10
|
+
sign: Bool = False
|
|
11
|
+
verify: Bool = False
|
|
12
|
+
deriveKey: Bool = False
|
|
13
|
+
deriveBits: Bool = False
|
|
14
|
+
wrapKey: Bool = False
|
|
15
|
+
unwrapKey: Bool = False
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class CryptoCipher {
|
|
19
|
+
RsaOaep(label: Option[String] = None)
|
|
20
|
+
AesCtr(counter: Buffer, length: Int)
|
|
21
|
+
AesCbc(initializationVector: Buffer)
|
|
22
|
+
AesGcm(initializationVector: Buffer, additionalData: Option[Buffer] = None, tagLength: Int = 128)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class CryptoDeriver {
|
|
26
|
+
Ecdh(public: CryptoKey)
|
|
27
|
+
Hkdf(hash: CryptoDigest, salt: Buffer, info: Buffer)
|
|
28
|
+
Pbkdf2(hash: CryptoDigest, salt: Buffer, iterations: Int)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class CryptoGenerator {
|
|
32
|
+
HmacGenerator(hash: CryptoDigest, length: Option[Int])
|
|
33
|
+
AesCtrGenerator(length: Int)
|
|
34
|
+
AesCbcGenerator(length: Int)
|
|
35
|
+
AesGcmGenerator(length: Int)
|
|
36
|
+
AesKwGenerator(length: Int)
|
|
37
|
+
HkdfGenerator(hash: CryptoDigest, salt: Buffer, info: Buffer)
|
|
38
|
+
Pbkdf2Generator(hash: CryptoDigest, salt: Buffer, iterations: Int)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class CryptoSigner {
|
|
42
|
+
RsassaPkcs1V15
|
|
43
|
+
RsaPss(saltLength: Int)
|
|
44
|
+
Ecdsa(hash: CryptoDigest)
|
|
45
|
+
Hmac
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
data CryptoDigest {
|
|
49
|
+
Sha512
|
|
50
|
+
Sha384
|
|
51
|
+
Sha256
|
|
52
|
+
Sha1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
extend self: SubtleCrypto {
|
|
56
|
+
|
|
57
|
+
encrypt(algorithm: CryptoCipher, key: CryptoKey, data: Buffer): Buffer
|
|
58
|
+
target js async """
|
|
59
|
+
const a = internalAlgorithm_(algorithm_);
|
|
60
|
+
return new DataView(await self_.encrypt(a, key_, data_));
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
decrypt(algorithm: CryptoCipher, key: CryptoKey, data: Buffer): Buffer
|
|
64
|
+
target js async """
|
|
65
|
+
const a = internalAlgorithm_(algorithm_);
|
|
66
|
+
return new DataView(await self_.decrypt(a, key_, data_));
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
sign(algorithm: CryptoSigner, key: CryptoKey, data: Buffer): Buffer
|
|
70
|
+
target js async """
|
|
71
|
+
const a = internalAlgorithm_(algorithm_);
|
|
72
|
+
return new DataView(await self_.sign(a, key_, data_));
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
verify(algorithm: CryptoSigner, key: CryptoKey, data: Buffer): Bool
|
|
76
|
+
target js async """
|
|
77
|
+
const a = internalAlgorithm_(algorithm_);
|
|
78
|
+
return await self_.verify(a, key_, data_);
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
digest(algorithm: CryptoDigest, data: Buffer): Buffer
|
|
82
|
+
target js async """
|
|
83
|
+
const a = internalAlgorithm_(algorithm_);
|
|
84
|
+
return new DataView(await self_.digest(a, data_));
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
deriveBits(algorithm: CryptoDeriver, key: CryptoKey, bits: Int): Buffer
|
|
88
|
+
target js async """
|
|
89
|
+
const a = internalAlgorithm_(algorithm_);
|
|
90
|
+
return new DataView(await self_.deriveBits(a, key_, bits_));
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
deriveKey(
|
|
94
|
+
algorithm: CryptoDeriver
|
|
95
|
+
baseKey: CryptoKey
|
|
96
|
+
derivedKeyAlgorithm: CryptoGenerator
|
|
97
|
+
extractable: Bool
|
|
98
|
+
keyUsages: CryptoKeyUsages
|
|
99
|
+
): CryptoKey
|
|
100
|
+
target js async """
|
|
101
|
+
const a = internalAlgorithm_(algorithm_);
|
|
102
|
+
const d = internalAlgorithm_(derivedKeyAlgorithm_);
|
|
103
|
+
const u = internalKeyUsages_(keyUsages_);
|
|
104
|
+
return await self_.deriveKey(a, baseKey_, d, extractable_, u);
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
internalAlgorithm(algorithm: JsValue): JsValue
|
|
110
|
+
target js sync """
|
|
111
|
+
if(algorithm_.Sha512) return 'SHA-512';
|
|
112
|
+
if(algorithm_.Sha384) return 'SHA-384';
|
|
113
|
+
if(algorithm_.Sha256) return 'SHA-256';
|
|
114
|
+
if(algorithm_.Sha1) return 'SHA-1';
|
|
115
|
+
if(algorithm_.RsassaPkcs1V15) return 'RSASSA-PKCS1-v1_5';
|
|
116
|
+
if(algorithm_.RsaPss) return {name: 'RSA-PSS', saltLength: algorithm_.saltLength_};
|
|
117
|
+
if(algorithm_.Ecdsa) return {name: 'ECDSA', hash: internalAlgorithm_(algorithm_.hash_)};
|
|
118
|
+
if(algorithm_.Hmac) return 'HMAC';
|
|
119
|
+
if(algorithm_.Ecdh) return {name: 'ECDH', public: algorithm_.public_};
|
|
120
|
+
if(algorithm_.Hkdf) return {name: 'HKDF', hash: internalAlgorithm_(algorithm_.hash_), salt: algorithm_.salt_, info: algorithm_.info_};
|
|
121
|
+
if(algorithm_.Pbkdf2) return {name: 'PBKDF2', hash: internalAlgorithm_(algorithm_.hash_), salt: algorithm_.salt_, iterations: algorithm_.iterations_};
|
|
122
|
+
if(algorithm_.RsaOaep) return {name: "RSA-OAEP", label: algorithm_.label_.value_};
|
|
123
|
+
if(algorithm_.AesCtr) return {name: "AES-CTR", counter: algorithm_.counter_, length: algorithm_.length_};
|
|
124
|
+
if(algorithm_.AesCbc) return {name: "AES-CBC", iv: algorithm_.initializationVector_};
|
|
125
|
+
if(algorithm_.AesGcm) return {name: "AES-GCM", iv: algorithm_.initializationVector_, additionalData: algorithm_.value_, tagLength: algorithm_.tagLength_};
|
|
126
|
+
// Turns out, generateKey has different parameters - think about naming
|
|
127
|
+
if(algorithm_.HmacGenerator) return {name: 'HMAC', hash: internalAlgorithm_(algorithm_.hash_), length: algorithm_.length_.value_};
|
|
128
|
+
if(algorithm_.AesCtrGenerator) return {name: 'AES-CTR', length: algorithm_.length_};
|
|
129
|
+
if(algorithm_.AesCbcGenerator) return {name: 'AES-CBC', length: algorithm_.length_};
|
|
130
|
+
if(algorithm_.AesGcmGenerator) return {name: 'AES-GCM', length: algorithm_.length_};
|
|
131
|
+
if(algorithm_.AesKwGenerator) return {name: 'AES-KW', length: algorithm_.length_};
|
|
132
|
+
if(algorithm_.HkdfGenerator) return {name: 'HKDF', hash: internalAlgorithm_(algorithm_.hash_), salt: algorithm_.salt_, info: algorithm_.info_};
|
|
133
|
+
if(algorithm_.Pbkdf2Generator) return {name: 'PBKDF2', hash: internalAlgorithm_(algorithm_.hash_), salt: algorithm_.salt_, iterations: algorithm_.iterations_};
|
|
134
|
+
throw new Error('No such algorithm');
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
internalKeyUsages(keyUsages: JsValue): JsValue
|
|
138
|
+
target js sync """
|
|
139
|
+
const u = [];
|
|
140
|
+
if(keyUsages_.encrypt_) u.push('encrypt');
|
|
141
|
+
if(keyUsages_.decrypt_) u.push('decrypt');
|
|
142
|
+
if(keyUsages_.sign_) u.push('sign');
|
|
143
|
+
if(keyUsages_.verify_) u.push('verify');
|
|
144
|
+
if(keyUsages_.deriveKey_) u.push('deriveKey');
|
|
145
|
+
if(keyUsages_.deriveBits_) u.push('deriveBits');
|
|
146
|
+
if(keyUsages_.wrapKey_) u.push('wrapKey');
|
|
147
|
+
if(keyUsages_.unwrapKey_) u.push('unwrapKey');
|
|
148
|
+
return u;
|
|
149
|
+
"""
|
package/experimental/s3/S3.ff
CHANGED
|
@@ -1,50 +1,3 @@
|
|
|
1
|
-
import Digest from ff:core
|
|
2
|
-
|
|
3
|
-
nodeMain(system: NodeSystem) {
|
|
4
|
-
system.writeLine(Digest.sha256("".toBuffer()).toHex())
|
|
5
|
-
system.writeLine(Digest.hmacSha256("key".toBuffer(), "The quick brown fox jumps over the lazy dog".toBuffer()).toHex())
|
|
6
|
-
|
|
7
|
-
// Trying to reproduce the result from an example here
|
|
8
|
-
// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
|
|
9
|
-
let expectedHeader = "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class,Signature=98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd"
|
|
10
|
-
let amzDate = "20130524T000000Z" // TODO
|
|
11
|
-
let headerDate = "Fri, 24 May 2013 00:00:00 GMT" // TODO
|
|
12
|
-
let bucket = "examplebucket"
|
|
13
|
-
let body = "Welcome to Amazon S3.".toBuffer()
|
|
14
|
-
let actualHeader = makeS3AuthorizationHeader(
|
|
15
|
-
system = system
|
|
16
|
-
accessKeyId = "AKIAIOSFODNN7EXAMPLE"
|
|
17
|
-
secretAccessKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
|
18
|
-
region = "us-east-1"
|
|
19
|
-
bucket = bucket
|
|
20
|
-
objectKey = "test%24file.text" // TODO URI encoded "test$file.text"
|
|
21
|
-
body = body
|
|
22
|
-
canonicalHeaders = [
|
|
23
|
-
Pair("date", headerDate)
|
|
24
|
-
Pair("host", bucket + ".s3.amazonaws.com")
|
|
25
|
-
Pair("x-amz-date", amzDate)
|
|
26
|
-
Pair("x-amz-content-sha256", Digest.sha256(body).toHex())
|
|
27
|
-
Pair("x-amz-storage-class", "REDUCED_REDUNDANCY")
|
|
28
|
-
]
|
|
29
|
-
amzDate = amzDate
|
|
30
|
-
)
|
|
31
|
-
system.writeLine("")
|
|
32
|
-
system.writeLine("actual : " + actualHeader)
|
|
33
|
-
system.writeLine("")
|
|
34
|
-
system.writeLine("expected: " + expectedHeader)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/*put(system,
|
|
39
|
-
accessKeyId = ""
|
|
40
|
-
secretAccessKey = ""
|
|
41
|
-
region = "eu-central-1"
|
|
42
|
-
bucket = "firefly-site"
|
|
43
|
-
objectKey = "tmp/test2"
|
|
44
|
-
"Hello S3".toBuffer()
|
|
45
|
-
)*/
|
|
46
|
-
}
|
|
47
|
-
|
|
48
1
|
put(
|
|
49
2
|
system: NodeSystem
|
|
50
3
|
accessKeyId: String
|
|
@@ -53,24 +6,24 @@ put(
|
|
|
53
6
|
bucket: String
|
|
54
7
|
objectKey: String
|
|
55
8
|
body: Buffer
|
|
9
|
+
headers: List[Pair[String, String]]
|
|
56
10
|
): Unit {
|
|
57
|
-
let
|
|
58
|
-
let
|
|
11
|
+
let now = system.mainTask().now()
|
|
12
|
+
let amzDate = toIsoString(now)
|
|
59
13
|
let contentHash = Digest.sha256(body).toHex()
|
|
60
14
|
let host = bucket + ".s3.amazonaws.com"
|
|
15
|
+
let encodedKey = encode(objectKey)
|
|
61
16
|
let canonicalHeaders = [
|
|
62
|
-
Pair("date", headerDate)
|
|
63
17
|
Pair("host", host)
|
|
64
18
|
Pair("x-amz-date", amzDate)
|
|
65
19
|
Pair("x-amz-content-sha256", contentHash)
|
|
66
|
-
|
|
67
|
-
Pair("Content-Type", "text/plain")
|
|
20
|
+
...headers
|
|
68
21
|
]
|
|
69
|
-
let authenticationHeader = makeS3AuthorizationHeader(system, accessKeyId, secretAccessKey, region, bucket,
|
|
70
|
-
let
|
|
22
|
+
let authenticationHeader = makeS3AuthorizationHeader(system, accessKeyId, secretAccessKey, region, bucket, encodedKey, body, canonicalHeaders, amzDate)
|
|
23
|
+
let allHeaders = [...canonicalHeaders, Pair("Authorization", authenticationHeader)]
|
|
71
24
|
|
|
72
|
-
let url = "https://" + host + "/" +
|
|
73
|
-
let response = system.httpClient().fetch(url, method = "PUT", headers =
|
|
25
|
+
let url = "https://" + host + "/" + encodedKey
|
|
26
|
+
let response = system.httpClient().fetch(url, method = "PUT", headers = allHeaders, body = Some(HttpClient.bodyBuffer(body)), throw = False)
|
|
74
27
|
system.writeLine("" + response.status())
|
|
75
28
|
system.writeLine(response.statusText())
|
|
76
29
|
system.writeLine(response.readText())
|
|
@@ -82,21 +35,19 @@ makeS3AuthorizationHeader(
|
|
|
82
35
|
secretAccessKey: String
|
|
83
36
|
region: String
|
|
84
37
|
bucket: String
|
|
85
|
-
|
|
38
|
+
encodedKey: String
|
|
86
39
|
body: Buffer
|
|
87
40
|
canonicalHeaders: List[Pair[String, String]]
|
|
88
41
|
amzDate: String
|
|
89
42
|
): String {
|
|
90
|
-
let scopeDate = amzDate.slice(0, 8) // YYYYMMDD
|
|
91
|
-
|
|
92
43
|
// CanonicalRequest
|
|
93
44
|
let canonicalQuerystring = "";
|
|
94
|
-
let contentHash =
|
|
45
|
+
let contentHash = system.crypto().sha256(body).toHex()
|
|
95
46
|
let headers = canonicalHeaders.map {_.mapFirst {_.lower()}.mapSecond {_.trim()}}.sort()
|
|
96
47
|
let signedHeaders = headers.map {_.first}.join(";")
|
|
97
48
|
let canonicalRequest = [
|
|
98
49
|
"PUT"
|
|
99
|
-
"/" +
|
|
50
|
+
"/" + encodedKey
|
|
100
51
|
canonicalQuerystring
|
|
101
52
|
...headers.map {p => p.first + ":" + p.second}
|
|
102
53
|
""
|
|
@@ -105,15 +56,20 @@ makeS3AuthorizationHeader(
|
|
|
105
56
|
].join("\n")
|
|
106
57
|
|
|
107
58
|
// StringToSign
|
|
59
|
+
let scopeDate = amzDate.slice(0, 8) // YYYYMMDD
|
|
108
60
|
let credentialScope = [scopeDate, region, "s3", "aws4_request"].join("/")
|
|
109
61
|
let algorithm = "AWS4-HMAC-SHA256"
|
|
110
62
|
let stringToSign = [
|
|
111
63
|
algorithm
|
|
112
64
|
amzDate
|
|
113
65
|
credentialScope
|
|
114
|
-
|
|
66
|
+
system.crypto().sha256(canonicalRequest.toBuffer()).toHex()
|
|
115
67
|
].join("\n")
|
|
116
68
|
|
|
69
|
+
function hmacSha256(key: Buffer, message: String): Buffer {
|
|
70
|
+
system.crypto().hmacSha256(key, message.toBuffer())
|
|
71
|
+
}
|
|
72
|
+
|
|
117
73
|
// Signature
|
|
118
74
|
let signingKey = hmacSha256(hmacSha256(hmacSha256(hmacSha256(("AWS4" + secretAccessKey).toBuffer(), scopeDate), region), "s3"), "aws4_request")
|
|
119
75
|
let signature = hmacSha256(signingKey, stringToSign).toHex()
|
|
@@ -121,6 +77,17 @@ makeS3AuthorizationHeader(
|
|
|
121
77
|
algorithm + " Credential=" + accessKeyId + "/" + credentialScope + ",SignedHeaders=" + signedHeaders + ",Signature=" + signature
|
|
122
78
|
}
|
|
123
79
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
80
|
+
toIsoString(instant: Instant): String
|
|
81
|
+
target js sync """
|
|
82
|
+
const d = new Date(instant_ * 1000);
|
|
83
|
+
return d.toISOString().replaceAll(/[-:]|[.].../g, "");
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
encodeURIComponent(text: String): String
|
|
87
|
+
target js sync """
|
|
88
|
+
return encodeURIComponent(text_);
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
encode(text: String): String {
|
|
92
|
+
text.split('/').map {encodeURIComponent(_)}.join("/")
|
|
93
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import S3
|
|
2
|
+
|
|
3
|
+
nodeMain(system: NodeSystem) {
|
|
4
|
+
// Trying to reproduce the result from an example here
|
|
5
|
+
// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
|
|
6
|
+
let expectedHeader = "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class,Signature=98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd"
|
|
7
|
+
let amzDate = "20130524T000000Z" // TODO
|
|
8
|
+
let headerDate = "Fri, 24 May 2013 00:00:00 GMT" // TODO
|
|
9
|
+
let bucket = "examplebucket"
|
|
10
|
+
let body = "Welcome to Amazon S3.".toBuffer()
|
|
11
|
+
let actualHeader = S3.makeS3AuthorizationHeader(
|
|
12
|
+
system = system
|
|
13
|
+
accessKeyId = "AKIAIOSFODNN7EXAMPLE"
|
|
14
|
+
secretAccessKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
|
15
|
+
region = "us-east-1"
|
|
16
|
+
bucket = bucket
|
|
17
|
+
encodedKey = S3.encode("test$file.text")
|
|
18
|
+
body = body
|
|
19
|
+
canonicalHeaders = [
|
|
20
|
+
Pair("date", headerDate)
|
|
21
|
+
Pair("host", bucket + ".s3.amazonaws.com")
|
|
22
|
+
Pair("x-amz-date", amzDate)
|
|
23
|
+
Pair("x-amz-content-sha256", system.crypto().sha256(body).toHex())
|
|
24
|
+
Pair("x-amz-storage-class", "REDUCED_REDUNDANCY")
|
|
25
|
+
]
|
|
26
|
+
amzDate = amzDate
|
|
27
|
+
)
|
|
28
|
+
system.writeLine("")
|
|
29
|
+
system.writeLine("actual : " + actualHeader)
|
|
30
|
+
system.writeLine("")
|
|
31
|
+
system.writeLine("expected: " + expectedHeader)
|
|
32
|
+
|
|
33
|
+
system.writeLine("")
|
|
34
|
+
if(actualHeader == expectedHeader) {
|
|
35
|
+
system.writeLine("Test passed")
|
|
36
|
+
} else {
|
|
37
|
+
system.writeLine("Test failed")
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import S3
|
|
2
|
+
|
|
3
|
+
nodeMain(system: NodeSystem) {
|
|
4
|
+
S3.put(system,
|
|
5
|
+
accessKeyId = ""
|
|
6
|
+
secretAccessKey = ""
|
|
7
|
+
region = "eu-central-1"
|
|
8
|
+
bucket = "firefly-site"
|
|
9
|
+
objectKey = "tmp/test4"
|
|
10
|
+
"Hello S3 3".toBuffer()
|
|
11
|
+
headers = [
|
|
12
|
+
//Pair("x-amz-storage-class", "REDUCED_REDUNDANCY")
|
|
13
|
+
Pair("Content-Type", "text/plain")
|
|
14
|
+
]
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -46,7 +46,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
46
46
|
|
|
47
47
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
48
48
|
|
|
49
|
-
import * as
|
|
49
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
50
50
|
|
|
51
51
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
52
52
|
|
|
@@ -46,7 +46,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
46
46
|
|
|
47
47
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
48
48
|
|
|
49
|
-
import * as
|
|
49
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
50
50
|
|
|
51
51
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
52
52
|
|
|
@@ -119,7 +119,7 @@ export function Compiler(emitTarget_, task_, compilerModulePath_, jsOutputPath_,
|
|
|
119
119
|
return {emitTarget_, task_, compilerModulePath_, jsOutputPath_, packagePaths_, singleFilePackages_, virtualFiles_, cache_, lspHook_, phaseDurationDelta_, phaseDurations_};
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
export const coreImports_ = ff_core_List.List_map(["Any", "Array", "AssetSystem", "Atomic", "Bool", "BrowserSystem", "Buffer", "BuildSystem", "Channel", "Char", "Core", "
|
|
122
|
+
export const coreImports_ = ff_core_List.List_map(["Any", "Array", "AssetSystem", "Atomic", "Bool", "BrowserSystem", "Buffer", "BuildSystem", "Channel", "Char", "Core", "Crypto", "Duration", "Equal", "Error", "FileHandle", "Float", "HttpClient", "Instant", "Int", "IntMap", "Json", "JsValue", "JsSystem", "List", "Lock", "Log", "Map", "NodeSystem", "Nothing", "Option", "Ordering", "Pair", "Path", "Random", "Serializable", "Set", "Show", "SourceLocation", "Stream", "String", "StringMap", "Task", "Try", "Unit"], ((moduleName_) => {
|
|
123
123
|
return ff_compiler_Syntax.DImport(ff_compiler_Syntax.Location("<prelude>", 1, 1), moduleName_, ff_compiler_Syntax.PackagePair("ff", "core"), [], moduleName_)
|
|
124
124
|
}));
|
|
125
125
|
|
|
@@ -36,7 +36,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
36
36
|
|
|
37
37
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
38
38
|
|
|
39
|
-
import * as
|
|
39
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
40
40
|
|
|
41
41
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
42
42
|
|
|
@@ -24,7 +24,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
24
24
|
|
|
25
25
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
26
26
|
|
|
27
|
-
import * as
|
|
27
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
28
28
|
|
|
29
29
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
30
30
|
|
|
@@ -26,7 +26,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
26
26
|
|
|
27
27
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
28
28
|
|
|
29
|
-
import * as
|
|
29
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
32
32
|
|
|
@@ -30,7 +30,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
32
32
|
|
|
33
|
-
import * as
|
|
33
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
34
34
|
|
|
35
35
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
36
36
|
|
|
@@ -26,7 +26,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
26
26
|
|
|
27
27
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
28
28
|
|
|
29
|
-
import * as
|
|
29
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
34
34
|
|
|
35
35
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
36
36
|
|
|
37
|
-
import * as
|
|
37
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
38
38
|
|
|
39
39
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
40
40
|
|
|
@@ -30,7 +30,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
32
32
|
|
|
33
|
-
import * as
|
|
33
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
34
34
|
|
|
35
35
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
36
36
|
|
|
@@ -26,7 +26,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
26
26
|
|
|
27
27
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
28
28
|
|
|
29
|
-
import * as
|
|
29
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
32
32
|
|
|
@@ -30,7 +30,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
32
32
|
|
|
33
|
-
import * as
|
|
33
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
34
34
|
|
|
35
35
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
36
36
|
|
|
@@ -50,7 +50,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
50
50
|
|
|
51
51
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
52
52
|
|
|
53
|
-
import * as
|
|
53
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
54
54
|
|
|
55
55
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
56
56
|
|
|
@@ -26,7 +26,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
26
26
|
|
|
27
27
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
28
28
|
|
|
29
|
-
import * as
|
|
29
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
32
32
|
|
|
@@ -32,7 +32,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
32
32
|
|
|
33
33
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
34
34
|
|
|
35
|
-
import * as
|
|
35
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
36
36
|
|
|
37
37
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
38
38
|
|
|
@@ -26,7 +26,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
26
26
|
|
|
27
27
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
28
28
|
|
|
29
|
-
import * as
|
|
29
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
32
32
|
|
|
@@ -28,7 +28,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
28
28
|
|
|
29
29
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
30
30
|
|
|
31
|
-
import * as
|
|
31
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
32
32
|
|
|
33
33
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
34
34
|
|
|
@@ -26,7 +26,7 @@ import * as ff_core_Char from "../../ff/core/Char.mjs"
|
|
|
26
26
|
|
|
27
27
|
import * as ff_core_Core from "../../ff/core/Core.mjs"
|
|
28
28
|
|
|
29
|
-
import * as
|
|
29
|
+
import * as ff_core_Crypto from "../../ff/core/Crypto.mjs"
|
|
30
30
|
|
|
31
31
|
import * as ff_core_Duration from "../../ff/core/Duration.mjs"
|
|
32
32
|
|