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.
Files changed (80) hide show
  1. package/compiler/Compiler.ff +1 -1
  2. package/core/Crypto.ff +95 -0
  3. package/core/NodeSystem.ff +3 -0
  4. package/crypto/SubtleCrypto.ff +149 -0
  5. package/experimental/s3/S3.ff +32 -65
  6. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -0
  7. package/experimental/s3/S3TestPut.ff +16 -0
  8. package/output/js/ff/compiler/Builder.mjs +1 -1
  9. package/output/js/ff/compiler/Compiler.mjs +2 -2
  10. package/output/js/ff/compiler/Dependencies.mjs +1 -1
  11. package/output/js/ff/compiler/DependencyLock.mjs +1 -1
  12. package/output/js/ff/compiler/Deriver.mjs +1 -1
  13. package/output/js/ff/compiler/Dictionaries.mjs +1 -1
  14. package/output/js/ff/compiler/Environment.mjs +1 -1
  15. package/output/js/ff/compiler/Inference.mjs +1 -1
  16. package/output/js/ff/compiler/JsEmitter.mjs +1 -1
  17. package/output/js/ff/compiler/JsImporter.mjs +1 -1
  18. package/output/js/ff/compiler/LspHook.mjs +1 -1
  19. package/output/js/ff/compiler/Main.mjs +1 -1
  20. package/output/js/ff/compiler/ModuleCache.mjs +1 -1
  21. package/output/js/ff/compiler/Parser.mjs +1 -1
  22. package/output/js/ff/compiler/Patterns.mjs +1 -1
  23. package/output/js/ff/compiler/Resolver.mjs +1 -1
  24. package/output/js/ff/compiler/Substitution.mjs +1 -1
  25. package/output/js/ff/compiler/Syntax.mjs +1 -1
  26. package/output/js/ff/compiler/Token.mjs +1 -1
  27. package/output/js/ff/compiler/Tokenizer.mjs +1 -1
  28. package/output/js/ff/compiler/Unification.mjs +1 -1
  29. package/output/js/ff/compiler/Wildcards.mjs +1 -1
  30. package/output/js/ff/compiler/Workspace.mjs +1 -1
  31. package/output/js/ff/core/Any.mjs +1 -1
  32. package/output/js/ff/core/Array.mjs +1 -1
  33. package/output/js/ff/core/AssetSystem.mjs +1 -1
  34. package/output/js/ff/core/Atomic.mjs +1 -1
  35. package/output/js/ff/core/Bool.mjs +1 -1
  36. package/output/js/ff/core/BrowserSystem.mjs +1 -1
  37. package/output/js/ff/core/Buffer.mjs +1 -1
  38. package/output/js/ff/core/BuildSystem.mjs +1 -1
  39. package/output/js/ff/core/Channel.mjs +1 -1
  40. package/output/js/ff/core/Char.mjs +1 -1
  41. package/output/js/ff/core/Core.mjs +1 -1
  42. package/output/js/ff/core/Crypto.mjs +275 -0
  43. package/output/js/ff/core/Duration.mjs +1 -1
  44. package/output/js/ff/core/Equal.mjs +1 -1
  45. package/output/js/ff/core/Error.mjs +1 -1
  46. package/output/js/ff/core/FileHandle.mjs +1 -1
  47. package/output/js/ff/core/Float.mjs +1 -1
  48. package/output/js/ff/core/HttpClient.mjs +1 -1
  49. package/output/js/ff/core/Instant.mjs +1 -1
  50. package/output/js/ff/core/Int.mjs +1 -1
  51. package/output/js/ff/core/IntMap.mjs +1 -1
  52. package/output/js/ff/core/JsSystem.mjs +1 -1
  53. package/output/js/ff/core/JsValue.mjs +1 -1
  54. package/output/js/ff/core/Json.mjs +1 -1
  55. package/output/js/ff/core/List.mjs +1 -1
  56. package/output/js/ff/core/Lock.mjs +1 -1
  57. package/output/js/ff/core/Log.mjs +1 -1
  58. package/output/js/ff/core/Map.mjs +1 -1
  59. package/output/js/ff/core/NodeSystem.mjs +9 -1
  60. package/output/js/ff/core/Nothing.mjs +1 -1
  61. package/output/js/ff/core/Option.mjs +1 -1
  62. package/output/js/ff/core/Ordering.mjs +1 -1
  63. package/output/js/ff/core/Pair.mjs +1 -1
  64. package/output/js/ff/core/Path.mjs +1 -1
  65. package/output/js/ff/core/Random.mjs +1 -1
  66. package/output/js/ff/core/RbMap.mjs +1 -1
  67. package/output/js/ff/core/Serializable.mjs +1 -1
  68. package/output/js/ff/core/Set.mjs +1 -1
  69. package/output/js/ff/core/Show.mjs +1 -1
  70. package/output/js/ff/core/SourceLocation.mjs +1 -1
  71. package/output/js/ff/core/Stream.mjs +1 -1
  72. package/output/js/ff/core/String.mjs +1 -1
  73. package/output/js/ff/core/StringMap.mjs +1 -1
  74. package/output/js/ff/core/Task.mjs +1 -1
  75. package/output/js/ff/core/Try.mjs +1 -1
  76. package/output/js/ff/core/Unit.mjs +1 -1
  77. package/package.json +1 -1
  78. package/vscode/package.json +1 -1
  79. package/core/Digest.ff +0 -51
  80. package/output/js/ff/core/Digest.mjs +0 -190
@@ -66,7 +66,7 @@ coreImports: List[DImport] =
66
66
  "Channel"
67
67
  "Char"
68
68
  "Core"
69
- "Digest"
69
+ "Crypto"
70
70
  "Duration"
71
71
  "Equal"
72
72
  "Error"
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
+ """
@@ -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
+ """
@@ -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 amzDate = "20240523T122636Z" // TODO
58
- let headerDate = "Thu May 23 2024 12:20:09 GMT" // TODO
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
- Pair("x-amz-storage-class", "REDUCED_REDUNDANCY")
67
- Pair("Content-Type", "text/plain")
20
+ ...headers
68
21
  ]
69
- let authenticationHeader = makeS3AuthorizationHeader(system, accessKeyId, secretAccessKey, region, bucket, objectKey, body, canonicalHeaders, amzDate)
70
- let headers = [...canonicalHeaders, Pair("Authorization", authenticationHeader)]
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 + "/" + objectKey
73
- let response = system.httpClient().fetch(url, method = "PUT", headers = headers, body = Some(HttpClient.bodyBuffer(body)), throw = False)
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
- objectKey: String
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 = Digest.sha256(body).toHex()
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
- "/" + objectKey
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
- Digest.sha256(canonicalRequest.toBuffer()).toHex()
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
- hmacSha256(key: Buffer, message: String): Buffer {
125
- Digest.hmacSha256(key, message.toBuffer()).toBuffer()
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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", "Digest", "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_) => {
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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 ff_core_Digest from "../../ff/core/Digest.mjs"
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