signedby 0.1.0__tar.gz
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.
- signedby-0.1.0/Cargo.lock +329 -0
- signedby-0.1.0/Cargo.toml +24 -0
- signedby-0.1.0/LICENSE +164 -0
- signedby-0.1.0/PKG-INFO +121 -0
- signedby-0.1.0/README.md +97 -0
- signedby-0.1.0/pyproject.toml +34 -0
- signedby-0.1.0/python/signedby/__init__.py +32 -0
- signedby-0.1.0/python/signedby/agent.py +124 -0
- signedby-0.1.0/python/signedby/client.py +132 -0
- signedby-0.1.0/python/signedby/errors.py +75 -0
- signedby-0.1.0/src/lib.rs +125 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.5.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "base64"
|
|
13
|
+
version = "0.21.7"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "bech32"
|
|
19
|
+
version = "0.11.1"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "bitflags"
|
|
25
|
+
version = "2.11.1"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "cfg-if"
|
|
31
|
+
version = "1.0.4"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "heck"
|
|
37
|
+
version = "0.4.1"
|
|
38
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
39
|
+
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
|
40
|
+
|
|
41
|
+
[[package]]
|
|
42
|
+
name = "hex"
|
|
43
|
+
version = "0.4.3"
|
|
44
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
45
|
+
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
46
|
+
|
|
47
|
+
[[package]]
|
|
48
|
+
name = "indoc"
|
|
49
|
+
version = "2.0.7"
|
|
50
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
51
|
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
52
|
+
dependencies = [
|
|
53
|
+
"rustversion",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[[package]]
|
|
57
|
+
name = "itoa"
|
|
58
|
+
version = "1.0.18"
|
|
59
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
60
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
61
|
+
|
|
62
|
+
[[package]]
|
|
63
|
+
name = "libc"
|
|
64
|
+
version = "0.2.185"
|
|
65
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
66
|
+
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
|
|
67
|
+
|
|
68
|
+
[[package]]
|
|
69
|
+
name = "lock_api"
|
|
70
|
+
version = "0.4.14"
|
|
71
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
72
|
+
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
|
73
|
+
dependencies = [
|
|
74
|
+
"scopeguard",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
[[package]]
|
|
78
|
+
name = "memchr"
|
|
79
|
+
version = "2.8.0"
|
|
80
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
81
|
+
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
82
|
+
|
|
83
|
+
[[package]]
|
|
84
|
+
name = "memoffset"
|
|
85
|
+
version = "0.9.1"
|
|
86
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
88
|
+
dependencies = [
|
|
89
|
+
"autocfg",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[[package]]
|
|
93
|
+
name = "once_cell"
|
|
94
|
+
version = "1.21.4"
|
|
95
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
96
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
97
|
+
|
|
98
|
+
[[package]]
|
|
99
|
+
name = "parking_lot"
|
|
100
|
+
version = "0.12.5"
|
|
101
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
102
|
+
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
|
103
|
+
dependencies = [
|
|
104
|
+
"lock_api",
|
|
105
|
+
"parking_lot_core",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
[[package]]
|
|
109
|
+
name = "parking_lot_core"
|
|
110
|
+
version = "0.9.12"
|
|
111
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
112
|
+
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
|
113
|
+
dependencies = [
|
|
114
|
+
"cfg-if",
|
|
115
|
+
"libc",
|
|
116
|
+
"redox_syscall",
|
|
117
|
+
"smallvec",
|
|
118
|
+
"windows-link",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "portable-atomic"
|
|
123
|
+
version = "1.13.1"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
126
|
+
|
|
127
|
+
[[package]]
|
|
128
|
+
name = "proc-macro2"
|
|
129
|
+
version = "1.0.106"
|
|
130
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
131
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
132
|
+
dependencies = [
|
|
133
|
+
"unicode-ident",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
[[package]]
|
|
137
|
+
name = "pyo3"
|
|
138
|
+
version = "0.20.3"
|
|
139
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
140
|
+
checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233"
|
|
141
|
+
dependencies = [
|
|
142
|
+
"cfg-if",
|
|
143
|
+
"indoc",
|
|
144
|
+
"libc",
|
|
145
|
+
"memoffset",
|
|
146
|
+
"parking_lot",
|
|
147
|
+
"portable-atomic",
|
|
148
|
+
"pyo3-build-config",
|
|
149
|
+
"pyo3-ffi",
|
|
150
|
+
"pyo3-macros",
|
|
151
|
+
"unindent",
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
[[package]]
|
|
155
|
+
name = "pyo3-build-config"
|
|
156
|
+
version = "0.20.3"
|
|
157
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
158
|
+
checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7"
|
|
159
|
+
dependencies = [
|
|
160
|
+
"once_cell",
|
|
161
|
+
"target-lexicon",
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
[[package]]
|
|
165
|
+
name = "pyo3-ffi"
|
|
166
|
+
version = "0.20.3"
|
|
167
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
168
|
+
checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa"
|
|
169
|
+
dependencies = [
|
|
170
|
+
"libc",
|
|
171
|
+
"pyo3-build-config",
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
[[package]]
|
|
175
|
+
name = "pyo3-macros"
|
|
176
|
+
version = "0.20.3"
|
|
177
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
178
|
+
checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158"
|
|
179
|
+
dependencies = [
|
|
180
|
+
"proc-macro2",
|
|
181
|
+
"pyo3-macros-backend",
|
|
182
|
+
"quote",
|
|
183
|
+
"syn",
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
[[package]]
|
|
187
|
+
name = "pyo3-macros-backend"
|
|
188
|
+
version = "0.20.3"
|
|
189
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
190
|
+
checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185"
|
|
191
|
+
dependencies = [
|
|
192
|
+
"heck",
|
|
193
|
+
"proc-macro2",
|
|
194
|
+
"pyo3-build-config",
|
|
195
|
+
"quote",
|
|
196
|
+
"syn",
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
[[package]]
|
|
200
|
+
name = "quote"
|
|
201
|
+
version = "1.0.45"
|
|
202
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
203
|
+
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
|
204
|
+
dependencies = [
|
|
205
|
+
"proc-macro2",
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
[[package]]
|
|
209
|
+
name = "redox_syscall"
|
|
210
|
+
version = "0.5.18"
|
|
211
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
212
|
+
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
|
213
|
+
dependencies = [
|
|
214
|
+
"bitflags",
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
[[package]]
|
|
218
|
+
name = "rustversion"
|
|
219
|
+
version = "1.0.22"
|
|
220
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
221
|
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
222
|
+
|
|
223
|
+
[[package]]
|
|
224
|
+
name = "scopeguard"
|
|
225
|
+
version = "1.2.0"
|
|
226
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
227
|
+
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
228
|
+
|
|
229
|
+
[[package]]
|
|
230
|
+
name = "serde"
|
|
231
|
+
version = "1.0.228"
|
|
232
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
233
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
234
|
+
dependencies = [
|
|
235
|
+
"serde_core",
|
|
236
|
+
"serde_derive",
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
[[package]]
|
|
240
|
+
name = "serde_core"
|
|
241
|
+
version = "1.0.228"
|
|
242
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
243
|
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
244
|
+
dependencies = [
|
|
245
|
+
"serde_derive",
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
[[package]]
|
|
249
|
+
name = "serde_derive"
|
|
250
|
+
version = "1.0.228"
|
|
251
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
252
|
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
253
|
+
dependencies = [
|
|
254
|
+
"proc-macro2",
|
|
255
|
+
"quote",
|
|
256
|
+
"syn",
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
[[package]]
|
|
260
|
+
name = "serde_json"
|
|
261
|
+
version = "1.0.149"
|
|
262
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
263
|
+
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
|
264
|
+
dependencies = [
|
|
265
|
+
"itoa",
|
|
266
|
+
"memchr",
|
|
267
|
+
"serde",
|
|
268
|
+
"serde_core",
|
|
269
|
+
"zmij",
|
|
270
|
+
]
|
|
271
|
+
|
|
272
|
+
[[package]]
|
|
273
|
+
name = "signedby-python"
|
|
274
|
+
version = "0.1.0"
|
|
275
|
+
dependencies = [
|
|
276
|
+
"base64",
|
|
277
|
+
"bech32",
|
|
278
|
+
"hex",
|
|
279
|
+
"pyo3",
|
|
280
|
+
"serde",
|
|
281
|
+
"serde_json",
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
[[package]]
|
|
285
|
+
name = "smallvec"
|
|
286
|
+
version = "1.15.1"
|
|
287
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
288
|
+
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
289
|
+
|
|
290
|
+
[[package]]
|
|
291
|
+
name = "syn"
|
|
292
|
+
version = "2.0.117"
|
|
293
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
|
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
|
295
|
+
dependencies = [
|
|
296
|
+
"proc-macro2",
|
|
297
|
+
"quote",
|
|
298
|
+
"unicode-ident",
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
[[package]]
|
|
302
|
+
name = "target-lexicon"
|
|
303
|
+
version = "0.12.16"
|
|
304
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
305
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
306
|
+
|
|
307
|
+
[[package]]
|
|
308
|
+
name = "unicode-ident"
|
|
309
|
+
version = "1.0.24"
|
|
310
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
311
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
312
|
+
|
|
313
|
+
[[package]]
|
|
314
|
+
name = "unindent"
|
|
315
|
+
version = "0.2.4"
|
|
316
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
317
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
318
|
+
|
|
319
|
+
[[package]]
|
|
320
|
+
name = "windows-link"
|
|
321
|
+
version = "0.2.1"
|
|
322
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
323
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
324
|
+
|
|
325
|
+
[[package]]
|
|
326
|
+
name = "zmij"
|
|
327
|
+
version = "1.0.21"
|
|
328
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
329
|
+
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "signedby-python"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Python bindings for SignedByMe SDK"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
[lib]
|
|
10
|
+
name = "signedby"
|
|
11
|
+
crate-type = ["cdylib"]
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
|
14
|
+
# PyO3 for Python bindings
|
|
15
|
+
pyo3 = { version = "0.20", features = ["extension-module"] }
|
|
16
|
+
|
|
17
|
+
# Serialization
|
|
18
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
19
|
+
serde_json = "1.0"
|
|
20
|
+
|
|
21
|
+
# Encoding
|
|
22
|
+
hex = "0.4"
|
|
23
|
+
bech32 = "0.11"
|
|
24
|
+
base64 = "0.21"
|
signedby-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
SIGNEDBYME SOURCE-AVAILABLE LICENSE v1.0
|
|
2
|
+
(“SSAL-1.0”)
|
|
3
|
+
|
|
4
|
+
Copyright © 2025–2026 Privacy Lion LLC (d/b/a SignedByMe).
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
This license is “source-available.” It is NOT an OSI Open Source license prior to
|
|
8
|
+
the Change Date. On the Change Date, the Licensed Work automatically becomes
|
|
9
|
+
available under the Apache License, Version 2.0, and the restrictions in this
|
|
10
|
+
license terminate.
|
|
11
|
+
|
|
12
|
+
----------------------------------------------------------------------
|
|
13
|
+
1. DEFINITIONS
|
|
14
|
+
----------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
“Licensor” means Privacy Lion LLC (d/b/a SignedByMe).
|
|
17
|
+
|
|
18
|
+
“Licensed Work” means all source code, build scripts, configuration, and other
|
|
19
|
+
materials included in this repository, including iOS, Android, and API/server code.
|
|
20
|
+
|
|
21
|
+
“Derivative Work” means any work based on or derived from the Licensed Work.
|
|
22
|
+
|
|
23
|
+
“Distribute” means to provide, publish, sublicense, sell, license, or otherwise
|
|
24
|
+
make the Licensed Work (or any Derivative Work) available to any third party,
|
|
25
|
+
including by publishing to an application store or distributing binaries.
|
|
26
|
+
|
|
27
|
+
“Make Available” means to operate the Licensed Work (or any Derivative Work) as a
|
|
28
|
+
network-accessible service for the benefit of third parties, including offering a
|
|
29
|
+
hosted API or hosted service.
|
|
30
|
+
|
|
31
|
+
“Competing Application” means any application, service, or product (including any
|
|
32
|
+
hosted API) offered to third parties that provides substantially similar
|
|
33
|
+
functionality to SignedByMe as a core or significant feature, including any
|
|
34
|
+
combination of:
|
|
35
|
+
|
|
36
|
+
(a) key-based signature authorization flows (e.g., DID/private-key signing
|
|
37
|
+
performed locally by a user);
|
|
38
|
+
|
|
39
|
+
(b) paid-login, payment-gated, or otherwise monetized authorization; and/or
|
|
40
|
+
|
|
41
|
+
(c) issuance of authentication artifacts (including OIDC/OAuth-compatible tokens
|
|
42
|
+
or functional equivalents) based on such authorization/proofs.
|
|
43
|
+
|
|
44
|
+
----------------------------------------------------------------------
|
|
45
|
+
2. LICENSE GRANT (NON-COMPETING USE)
|
|
46
|
+
----------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
Subject to the restriction in Section 3, the Licensor grants you a worldwide,
|
|
49
|
+
non-exclusive, royalty-free license to copy, modify, and create Derivative Works
|
|
50
|
+
of the Licensed Work, and to Distribute the Licensed Work or Derivative Works,
|
|
51
|
+
solely for Non-Competing Use.
|
|
52
|
+
|
|
53
|
+
“Non-Competing Use” means any use that is NOT:
|
|
54
|
+
(i) Distributing the Licensed Work (or any Derivative Work) as part of a
|
|
55
|
+
Competing Application; and NOT
|
|
56
|
+
(ii) Making Available the Licensed Work (or any Derivative Work) as part of a
|
|
57
|
+
Competing Application.
|
|
58
|
+
|
|
59
|
+
Examples of permitted Non-Competing Use include: evaluation, testing, security
|
|
60
|
+
research, personal projects, internal company tools, contributions back to this
|
|
61
|
+
repository, and integration into unrelated products/services that are not a
|
|
62
|
+
Competing Application.
|
|
63
|
+
|
|
64
|
+
----------------------------------------------------------------------
|
|
65
|
+
3. RESTRICTION (NO COMPETING APPLICATIONS/SERVICES)
|
|
66
|
+
----------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
You may NOT Distribute or Make Available the Licensed Work (or any Derivative Work)
|
|
69
|
+
as part of a Competing Application.
|
|
70
|
+
|
|
71
|
+
This restriction applies regardless of whether the Competing Application is free
|
|
72
|
+
or paid, open-source or proprietary, self-hosted or hosted for others.
|
|
73
|
+
|
|
74
|
+
----------------------------------------------------------------------
|
|
75
|
+
4. TERMINATION
|
|
76
|
+
----------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
Any violation of Section 3 automatically terminates the license granted to you
|
|
79
|
+
under this Agreement. Upon termination, you must immediately cease use, Distribute
|
|
80
|
+
no further copies, and stop Making Available any service based on the Licensed Work.
|
|
81
|
+
|
|
82
|
+
----------------------------------------------------------------------
|
|
83
|
+
5. CHANGE DATE AND CHANGE LICENSE
|
|
84
|
+
----------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
Change Date: 2030-02-18
|
|
87
|
+
|
|
88
|
+
On the Change Date, the restrictions in this license automatically terminate and
|
|
89
|
+
the Licensed Work becomes fully available under the Apache License, Version 2.0
|
|
90
|
+
(no further action required from Licensor or the user).
|
|
91
|
+
|
|
92
|
+
Change License: Apache License, Version 2.0
|
|
93
|
+
|
|
94
|
+
A copy of the Apache 2.0 license text is provided in this repository as
|
|
95
|
+
LICENSE-APACHE.
|
|
96
|
+
|
|
97
|
+
----------------------------------------------------------------------
|
|
98
|
+
6. CONTRIBUTIONS
|
|
99
|
+
----------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
If you submit a contribution (including code, documentation, or other materials)
|
|
102
|
+
to the Licensed Work (e.g., via pull request, patch, or issue attachment), you
|
|
103
|
+
grant Licensor a perpetual, worldwide, non-exclusive, royalty-free license to use,
|
|
104
|
+
reproduce, modify, distribute, and sublicense your contribution as part of the
|
|
105
|
+
Licensed Work under the terms of this license and (after the Change Date) under
|
|
106
|
+
the Change License.
|
|
107
|
+
|
|
108
|
+
----------------------------------------------------------------------
|
|
109
|
+
7. TRADEMARKS
|
|
110
|
+
----------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
This license does not grant any rights in the trademarks, service marks, trade
|
|
113
|
+
names, or logos of Licensor, including “SignedByMe” and “Privacy Lion”.
|
|
114
|
+
|
|
115
|
+
----------------------------------------------------------------------
|
|
116
|
+
8. DISCLAIMER OF WARRANTY
|
|
117
|
+
----------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
THE LICENSED WORK IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
120
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
121
|
+
FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. YOU BEAR THE RISK OF USING IT.
|
|
122
|
+
|
|
123
|
+
----------------------------------------------------------------------
|
|
124
|
+
9. LIMITATION OF LIABILITY
|
|
125
|
+
----------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT WILL LICENSOR BE LIABLE FOR
|
|
128
|
+
ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR ANY LOSS
|
|
129
|
+
OF PROFITS, REVENUE, DATA, OR GOODWILL, ARISING OUT OF OR RELATED TO THIS LICENSE
|
|
130
|
+
OR THE USE OF THE LICENSED WORK, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
131
|
+
|
|
132
|
+
----------------------------------------------------------------------
|
|
133
|
+
10. GOVERNING LAW
|
|
134
|
+
----------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
This license shall be governed by the laws of the State of Florida, without regard
|
|
137
|
+
to conflict of laws principles. Venue for any dispute arising under this license
|
|
138
|
+
shall be in state or federal courts located in Miami-Dade County, Florida, and the
|
|
139
|
+
parties consent to personal jurisdiction there.
|
|
140
|
+
|
|
141
|
+
----------------------------------------------------------------------
|
|
142
|
+
11. SEVERABILITY
|
|
143
|
+
----------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
If any provision of this license is held to be invalid, illegal, or unenforceable,
|
|
146
|
+
the remaining provisions shall remain in full force and effect. Any invalid,
|
|
147
|
+
illegal, or unenforceable provision shall be deemed modified to the minimum extent
|
|
148
|
+
necessary to make it valid and enforceable while preserving the parties’ intent as
|
|
149
|
+
closely as possible.
|
|
150
|
+
|
|
151
|
+
----------------------------------------------------------------------
|
|
152
|
+
12. ENTIRE AGREEMENT
|
|
153
|
+
----------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
This license constitutes the entire agreement between the parties with respect to
|
|
156
|
+
the Licensed Work and supersedes all prior or contemporaneous understandings,
|
|
157
|
+
whether written or oral.
|
|
158
|
+
|
|
159
|
+
----------------------------------------------------------------------
|
|
160
|
+
13. ACCEPTANCE
|
|
161
|
+
----------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
BY COPYING, MODIFYING, DISTRIBUTING, OR MAKING AVAILABLE THE LICENSED WORK, YOU
|
|
164
|
+
ACCEPT THE TERMS OF THIS LICENSE.
|
signedby-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: signedby
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Rust
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Topic :: Security :: Cryptography
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Summary: SignedByMe SDK - Self-signing digital signatures with zero-knowledge proofs
|
|
15
|
+
Keywords: identity,zkp,groth16,nostr,bitcoin,authentication
|
|
16
|
+
Author-email: Privacy Lion <contact@privacy-lion.com>
|
|
17
|
+
License: MIT
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
|
+
Project-URL: Documentation, https://docs.signedbyme.com
|
|
21
|
+
Project-URL: Homepage, https://signedbyme.com
|
|
22
|
+
Project-URL: Repository, https://github.com/PrivacyLion/SignedByMe
|
|
23
|
+
|
|
24
|
+
# SignedByMe Python SDK
|
|
25
|
+
|
|
26
|
+
Human-controlled identity for autonomous agents.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install signedby
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### For Agents - Authenticate to Enterprises
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from signedby import SignedByClient
|
|
40
|
+
|
|
41
|
+
# Load delegation from your human owner
|
|
42
|
+
client = SignedByClient.from_delegation("./delegation.json")
|
|
43
|
+
|
|
44
|
+
print(f"Your npub: {client.npub}")
|
|
45
|
+
print(f"Authorized for: {client.scopes}")
|
|
46
|
+
|
|
47
|
+
# Authenticate to an enterprise
|
|
48
|
+
token = await client.login(
|
|
49
|
+
client_id="acme-corp",
|
|
50
|
+
nonce="random_nonce_here"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Use the OIDC token
|
|
54
|
+
print(f"ID Token: {token.id_token}")
|
|
55
|
+
print(f"Subject: {token.sub}")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### For Agent Setup - Initialize Identity
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from signedby import SignedByAgent
|
|
62
|
+
|
|
63
|
+
# Initialize agent (creates DID if first run)
|
|
64
|
+
agent = SignedByAgent.init(storage_path="./agent_data")
|
|
65
|
+
|
|
66
|
+
print(f"Agent npub: {agent.npub}")
|
|
67
|
+
|
|
68
|
+
# Configure email mapping for enterprises
|
|
69
|
+
agent.set_email_mapping({
|
|
70
|
+
"amazon.com": "you@gmail.com",
|
|
71
|
+
"acme.com": "you@gmail.com"
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
# Connect to relay and watch for authorizations
|
|
75
|
+
agent.connect_relay("wss://relay.privacy-lion.com")
|
|
76
|
+
|
|
77
|
+
async for event in agent.watch_for_authorizations():
|
|
78
|
+
print(f"New authorization from: {event.enterprise}")
|
|
79
|
+
print(f"Scopes: {event.scopes}")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Error Handling
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from signedby import (
|
|
86
|
+
SignedByClient,
|
|
87
|
+
SignedByError,
|
|
88
|
+
DelegationRevokedError,
|
|
89
|
+
DelegationExpiredError,
|
|
90
|
+
ScopeDeniedError,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
token = await client.login(client_id="acme-corp", nonce=nonce)
|
|
95
|
+
except DelegationRevokedError:
|
|
96
|
+
print("Delegation was revoked. Contact your human owner.")
|
|
97
|
+
except DelegationExpiredError:
|
|
98
|
+
print("Delegation expired. Request renewal from your human owner.")
|
|
99
|
+
except ScopeDeniedError:
|
|
100
|
+
print("Not authorized for this enterprise.")
|
|
101
|
+
except SignedByError as e:
|
|
102
|
+
print(f"Authentication failed: {e}")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Requirements
|
|
106
|
+
|
|
107
|
+
- Python 3.9+
|
|
108
|
+
- Supported platforms: Linux (x86_64, aarch64), macOS (x86_64, arm64), Windows (x86_64)
|
|
109
|
+
|
|
110
|
+
## Documentation
|
|
111
|
+
|
|
112
|
+
- [SDK Quick Start](https://signedbyme.com/docs/sdk-quickstart.html)
|
|
113
|
+
- [API Reference](https://signedbyme.com/docs/api-reference.html)
|
|
114
|
+
- [Understanding Delegation](https://signedbyme.com/docs/delegation.html)
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
SignedByMe Source-Available License v1.0 (SSAL-1.0)
|
|
119
|
+
|
|
120
|
+
See [LICENSE](https://github.com/PrivacyLion/SignedByMe/blob/main/LICENSE) for details.
|
|
121
|
+
|
signedby-0.1.0/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# SignedByMe Python SDK
|
|
2
|
+
|
|
3
|
+
Human-controlled identity for autonomous agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install signedby
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### For Agents - Authenticate to Enterprises
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from signedby import SignedByClient
|
|
17
|
+
|
|
18
|
+
# Load delegation from your human owner
|
|
19
|
+
client = SignedByClient.from_delegation("./delegation.json")
|
|
20
|
+
|
|
21
|
+
print(f"Your npub: {client.npub}")
|
|
22
|
+
print(f"Authorized for: {client.scopes}")
|
|
23
|
+
|
|
24
|
+
# Authenticate to an enterprise
|
|
25
|
+
token = await client.login(
|
|
26
|
+
client_id="acme-corp",
|
|
27
|
+
nonce="random_nonce_here"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Use the OIDC token
|
|
31
|
+
print(f"ID Token: {token.id_token}")
|
|
32
|
+
print(f"Subject: {token.sub}")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### For Agent Setup - Initialize Identity
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from signedby import SignedByAgent
|
|
39
|
+
|
|
40
|
+
# Initialize agent (creates DID if first run)
|
|
41
|
+
agent = SignedByAgent.init(storage_path="./agent_data")
|
|
42
|
+
|
|
43
|
+
print(f"Agent npub: {agent.npub}")
|
|
44
|
+
|
|
45
|
+
# Configure email mapping for enterprises
|
|
46
|
+
agent.set_email_mapping({
|
|
47
|
+
"amazon.com": "you@gmail.com",
|
|
48
|
+
"acme.com": "you@gmail.com"
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
# Connect to relay and watch for authorizations
|
|
52
|
+
agent.connect_relay("wss://relay.privacy-lion.com")
|
|
53
|
+
|
|
54
|
+
async for event in agent.watch_for_authorizations():
|
|
55
|
+
print(f"New authorization from: {event.enterprise}")
|
|
56
|
+
print(f"Scopes: {event.scopes}")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Error Handling
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from signedby import (
|
|
63
|
+
SignedByClient,
|
|
64
|
+
SignedByError,
|
|
65
|
+
DelegationRevokedError,
|
|
66
|
+
DelegationExpiredError,
|
|
67
|
+
ScopeDeniedError,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
token = await client.login(client_id="acme-corp", nonce=nonce)
|
|
72
|
+
except DelegationRevokedError:
|
|
73
|
+
print("Delegation was revoked. Contact your human owner.")
|
|
74
|
+
except DelegationExpiredError:
|
|
75
|
+
print("Delegation expired. Request renewal from your human owner.")
|
|
76
|
+
except ScopeDeniedError:
|
|
77
|
+
print("Not authorized for this enterprise.")
|
|
78
|
+
except SignedByError as e:
|
|
79
|
+
print(f"Authentication failed: {e}")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Requirements
|
|
83
|
+
|
|
84
|
+
- Python 3.9+
|
|
85
|
+
- Supported platforms: Linux (x86_64, aarch64), macOS (x86_64, arm64), Windows (x86_64)
|
|
86
|
+
|
|
87
|
+
## Documentation
|
|
88
|
+
|
|
89
|
+
- [SDK Quick Start](https://signedbyme.com/docs/sdk-quickstart.html)
|
|
90
|
+
- [API Reference](https://signedbyme.com/docs/api-reference.html)
|
|
91
|
+
- [Understanding Delegation](https://signedbyme.com/docs/delegation.html)
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
SignedByMe Source-Available License v1.0 (SSAL-1.0)
|
|
96
|
+
|
|
97
|
+
See [LICENSE](https://github.com/PrivacyLion/SignedByMe/blob/main/LICENSE) for details.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.4,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "signedby"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "SignedByMe SDK - Self-signing digital signatures with zero-knowledge proofs"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Privacy Lion", email = "contact@privacy-lion.com" }
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.9",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Rust",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Topic :: Security :: Cryptography",
|
|
24
|
+
]
|
|
25
|
+
keywords = ["identity", "zkp", "groth16", "nostr", "bitcoin", "authentication"]
|
|
26
|
+
requires-python = ">=3.9"
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://signedbyme.com"
|
|
30
|
+
Documentation = "https://docs.signedbyme.com"
|
|
31
|
+
Repository = "https://github.com/PrivacyLion/SignedByMe"
|
|
32
|
+
|
|
33
|
+
[tool.maturin]
|
|
34
|
+
features = ["pyo3/extension-module"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SignedByMe SDK - Human-controlled identity for autonomous agents.
|
|
3
|
+
|
|
4
|
+
This SDK allows agents to:
|
|
5
|
+
- Load delegated credentials from their human owner
|
|
6
|
+
- Generate Groth16 zero-knowledge proofs
|
|
7
|
+
- Authenticate to enterprises and receive OIDC tokens
|
|
8
|
+
- Manage NOSTR event publishing for audit trails
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .client import SignedByClient
|
|
12
|
+
from .agent import SignedByAgent
|
|
13
|
+
from .errors import (
|
|
14
|
+
SignedByError,
|
|
15
|
+
DelegationRevokedError,
|
|
16
|
+
DelegationExpiredError,
|
|
17
|
+
InvalidProofError,
|
|
18
|
+
MerkleRootExpiredError,
|
|
19
|
+
ScopeDeniedError,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
23
|
+
__all__ = [
|
|
24
|
+
"SignedByClient",
|
|
25
|
+
"SignedByAgent",
|
|
26
|
+
"SignedByError",
|
|
27
|
+
"DelegationRevokedError",
|
|
28
|
+
"DelegationExpiredError",
|
|
29
|
+
"InvalidProofError",
|
|
30
|
+
"MerkleRootExpiredError",
|
|
31
|
+
"ScopeDeniedError",
|
|
32
|
+
]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SignedByAgent - Agent initialization and management.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional, Dict, Any, AsyncIterator
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
# Import the Rust core via PyO3
|
|
12
|
+
from signedby._core import (
|
|
13
|
+
RustSignedByAgent,
|
|
14
|
+
init_agent_storage,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class AuthorizationEvent:
|
|
20
|
+
"""An authorization request from an enterprise (kind 28200)."""
|
|
21
|
+
enterprise: str
|
|
22
|
+
client_id: str
|
|
23
|
+
scopes: list[str]
|
|
24
|
+
event_id: str
|
|
25
|
+
created_at: int
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SignedByAgent:
|
|
29
|
+
"""
|
|
30
|
+
Agent for managing identity and watching for authorization requests.
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
agent = SignedByAgent.init(storage_path="./agent_data")
|
|
34
|
+
agent.set_email_mapping({"amazon.com": "me@gmail.com"})
|
|
35
|
+
agent.connect_relay("wss://relay.privacy-lion.com")
|
|
36
|
+
agent.watch_for_authorizations()
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, rust_agent: RustSignedByAgent):
|
|
40
|
+
self._rust = rust_agent
|
|
41
|
+
self._relay_connected = False
|
|
42
|
+
self._email_mapping: Dict[str, str] = {}
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def init(cls, storage_path: str | Path) -> SignedByAgent:
|
|
46
|
+
"""
|
|
47
|
+
Initialize a new agent or load existing identity.
|
|
48
|
+
|
|
49
|
+
Creates DID keys and stores them securely if this is first run.
|
|
50
|
+
Loads existing identity if storage already exists.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
storage_path: Directory for agent data (keys, witness cache)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
SignedByAgent instance
|
|
57
|
+
"""
|
|
58
|
+
storage_path = Path(storage_path)
|
|
59
|
+
storage_path.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
rust_agent = RustSignedByAgent.init(str(storage_path))
|
|
62
|
+
return cls(rust_agent)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def npub(self) -> str:
|
|
66
|
+
"""Get the agent's npub (public key in bech32 format)."""
|
|
67
|
+
return self._rust.npub()
|
|
68
|
+
|
|
69
|
+
def set_email_mapping(self, mapping: Dict[str, str]) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Set email mapping for enterprises.
|
|
72
|
+
|
|
73
|
+
This tells the agent which email to provide during Gate 1
|
|
74
|
+
enrollment for each enterprise domain.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
mapping: Dict of enterprise domain -> email address
|
|
78
|
+
e.g., {"amazon.com": "me@gmail.com"}
|
|
79
|
+
"""
|
|
80
|
+
self._email_mapping = mapping
|
|
81
|
+
self._rust.set_email_mapping(mapping)
|
|
82
|
+
|
|
83
|
+
def connect_relay(self, relay_url: str) -> None:
|
|
84
|
+
"""
|
|
85
|
+
Connect to a NOSTR relay.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
relay_url: WebSocket URL of the relay
|
|
89
|
+
e.g., "wss://relay.privacy-lion.com"
|
|
90
|
+
"""
|
|
91
|
+
self._rust.connect_relay(relay_url)
|
|
92
|
+
self._relay_connected = True
|
|
93
|
+
|
|
94
|
+
async def watch_for_authorizations(self) -> AsyncIterator[AuthorizationEvent]:
|
|
95
|
+
"""
|
|
96
|
+
Watch for kind 28200 authorization events addressed to this agent.
|
|
97
|
+
|
|
98
|
+
Yields:
|
|
99
|
+
AuthorizationEvent for each incoming authorization request
|
|
100
|
+
"""
|
|
101
|
+
if not self._relay_connected:
|
|
102
|
+
raise RuntimeError("Not connected to relay. Call connect_relay() first.")
|
|
103
|
+
|
|
104
|
+
async for event_json in self._rust.subscribe_authorizations():
|
|
105
|
+
event = json.loads(event_json)
|
|
106
|
+
yield AuthorizationEvent(
|
|
107
|
+
enterprise=event.get("enterprise", "unknown"),
|
|
108
|
+
client_id=event["client_id"],
|
|
109
|
+
scopes=event.get("scopes", []),
|
|
110
|
+
event_id=event["id"],
|
|
111
|
+
created_at=event["created_at"],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def get_activity_log(self, limit: int = 100) -> list[Dict[str, Any]]:
|
|
115
|
+
"""
|
|
116
|
+
Get recent agent activity (kinds 28101, 28102, 28103).
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
limit: Maximum number of events to return
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List of activity events
|
|
123
|
+
"""
|
|
124
|
+
return self._rust.get_activity_log(limit)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SignedByClient - Core client for agent authentication.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional, Dict, Any
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
# Import the Rust core via PyO3
|
|
12
|
+
from signedby._core import (
|
|
13
|
+
RustSignedByClient,
|
|
14
|
+
generate_proof,
|
|
15
|
+
verify_delegation,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class LoginToken:
|
|
21
|
+
"""OIDC token returned from successful authentication."""
|
|
22
|
+
id_token: str
|
|
23
|
+
token_type: str
|
|
24
|
+
expires_in: int
|
|
25
|
+
sub: str # npub in bech32
|
|
26
|
+
|
|
27
|
+
def is_expired(self) -> bool:
|
|
28
|
+
"""Check if the token has expired."""
|
|
29
|
+
import time
|
|
30
|
+
# Token includes iat, expires_in tells us duration
|
|
31
|
+
# For now, we track expiry client-side
|
|
32
|
+
return False # TODO: implement proper expiry tracking
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class LoginRequest:
|
|
37
|
+
"""Request parameters for login."""
|
|
38
|
+
client_id: str
|
|
39
|
+
nonce: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SignedByClient:
|
|
43
|
+
"""
|
|
44
|
+
Client for authenticating to enterprises using SignedByMe.
|
|
45
|
+
|
|
46
|
+
Usage:
|
|
47
|
+
client = SignedByClient.from_delegation("./delegation.json")
|
|
48
|
+
token = await client.login(client_id="acme-corp", nonce="random123")
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, rust_client: RustSignedByClient):
|
|
52
|
+
self._rust = rust_client
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_delegation(cls, path: str | Path) -> SignedByClient:
|
|
56
|
+
"""
|
|
57
|
+
Load a SignedByClient from a delegation file.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
path: Path to the delegation JSON file (kind 28250 event)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
SignedByClient instance ready for authentication
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
FileNotFoundError: If delegation file doesn't exist
|
|
67
|
+
SignedByError: If delegation is invalid or expired
|
|
68
|
+
"""
|
|
69
|
+
path = Path(path)
|
|
70
|
+
if not path.exists():
|
|
71
|
+
raise FileNotFoundError(f"Delegation file not found: {path}")
|
|
72
|
+
|
|
73
|
+
delegation_json = path.read_text()
|
|
74
|
+
rust_client = RustSignedByClient.from_delegation_json(delegation_json)
|
|
75
|
+
return cls(rust_client)
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def npub(self) -> str:
|
|
79
|
+
"""Get the agent's npub (public key in bech32 format)."""
|
|
80
|
+
return self._rust.npub()
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def scopes(self) -> Dict[str, list[str]]:
|
|
84
|
+
"""Get the delegation scopes (enterprise -> permissions mapping)."""
|
|
85
|
+
return self._rust.scopes()
|
|
86
|
+
|
|
87
|
+
async def login(
|
|
88
|
+
self,
|
|
89
|
+
client_id: str,
|
|
90
|
+
nonce: str,
|
|
91
|
+
*,
|
|
92
|
+
relay_url: str = "wss://relay.privacy-lion.com",
|
|
93
|
+
api_url: str = "https://api.beta.privacy-lion.com",
|
|
94
|
+
) -> LoginToken:
|
|
95
|
+
"""
|
|
96
|
+
Authenticate to an enterprise and receive an OIDC token.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
client_id: The enterprise's client ID
|
|
100
|
+
nonce: Random nonce for replay protection
|
|
101
|
+
relay_url: NOSTR relay URL (optional)
|
|
102
|
+
api_url: SignedByMe API URL (optional)
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
LoginToken containing the OIDC id_token
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
DelegationRevokedError: If delegation has been revoked
|
|
109
|
+
DelegationExpiredError: If delegation has expired
|
|
110
|
+
ScopeDeniedError: If client_id not in delegation scopes
|
|
111
|
+
MerkleRootExpiredError: If proof uses stale merkle root
|
|
112
|
+
"""
|
|
113
|
+
# Generate Groth16 proof
|
|
114
|
+
proof_result = await self._rust.generate_login_proof(client_id, nonce)
|
|
115
|
+
|
|
116
|
+
# Publish proof event to NOSTR (kind 28101)
|
|
117
|
+
await self._rust.publish_proof_event(relay_url, proof_result)
|
|
118
|
+
|
|
119
|
+
# Call API to verify and get token
|
|
120
|
+
token_response = await self._rust.verify_and_get_token(
|
|
121
|
+
api_url,
|
|
122
|
+
proof_result,
|
|
123
|
+
client_id,
|
|
124
|
+
nonce,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return LoginToken(
|
|
128
|
+
id_token=token_response["id_token"],
|
|
129
|
+
token_type=token_response.get("token_type", "Bearer"),
|
|
130
|
+
expires_in=token_response.get("expires_in", 3600),
|
|
131
|
+
sub=token_response["sub"],
|
|
132
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SignedByMe SDK Errors.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SignedByError(Exception):
|
|
7
|
+
"""Base exception for all SignedByMe errors."""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DelegationRevokedError(SignedByError):
|
|
12
|
+
"""
|
|
13
|
+
Raised when the delegation has been revoked.
|
|
14
|
+
|
|
15
|
+
The human owner published a kind 28251 revocation event.
|
|
16
|
+
Contact your human owner for a new delegation.
|
|
17
|
+
"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DelegationExpiredError(SignedByError):
|
|
22
|
+
"""
|
|
23
|
+
Raised when the delegation has expired.
|
|
24
|
+
|
|
25
|
+
The expires_at timestamp in the delegation has passed.
|
|
26
|
+
Request a renewed delegation from your human owner.
|
|
27
|
+
"""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class InvalidProofError(SignedByError):
|
|
32
|
+
"""
|
|
33
|
+
Raised when Groth16 proof verification fails.
|
|
34
|
+
|
|
35
|
+
This typically indicates corrupted proof data or
|
|
36
|
+
a mismatch between proof and public inputs.
|
|
37
|
+
"""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MerkleRootExpiredError(SignedByError):
|
|
42
|
+
"""
|
|
43
|
+
Raised when the proof references a stale merkle root.
|
|
44
|
+
|
|
45
|
+
The merkle root in the proof is not in the last 30 valid roots.
|
|
46
|
+
Refresh witness data and regenerate the proof.
|
|
47
|
+
"""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ScopeDeniedError(SignedByError):
|
|
52
|
+
"""
|
|
53
|
+
Raised when attempting to access an unauthorized enterprise.
|
|
54
|
+
|
|
55
|
+
The client_id is not in the delegation scopes.
|
|
56
|
+
Request an updated delegation that includes this enterprise.
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class RelayConnectionError(SignedByError):
|
|
62
|
+
"""
|
|
63
|
+
Raised when unable to connect to a NOSTR relay.
|
|
64
|
+
"""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ApiError(SignedByError):
|
|
69
|
+
"""
|
|
70
|
+
Raised when the SignedByMe API returns an error.
|
|
71
|
+
"""
|
|
72
|
+
def __init__(self, message: str, error_code: str = None, status_code: int = None):
|
|
73
|
+
super().__init__(message)
|
|
74
|
+
self.error_code = error_code
|
|
75
|
+
self.status_code = status_code
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
//! Python bindings for SignedByMe SDK
|
|
2
|
+
//!
|
|
3
|
+
//! Provides Groth16 proof verification and OIDC token validation for Python.
|
|
4
|
+
|
|
5
|
+
use pyo3::prelude::*;
|
|
6
|
+
use pyo3::exceptions::{PyValueError, PyRuntimeError};
|
|
7
|
+
use pyo3::types::PyDict;
|
|
8
|
+
|
|
9
|
+
/// Convert hex public key to bech32 npub format
|
|
10
|
+
#[pyfunction]
|
|
11
|
+
fn hex_to_npub(hex_pubkey: &str) -> PyResult<String> {
|
|
12
|
+
// Simple bech32 encoding for npub
|
|
13
|
+
let bytes = hex::decode(hex_pubkey)
|
|
14
|
+
.map_err(|e| PyValueError::new_err(format!("Invalid hex: {}", e)))?;
|
|
15
|
+
|
|
16
|
+
if bytes.len() != 32 {
|
|
17
|
+
return Err(PyValueError::new_err("Public key must be 32 bytes"));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Use bech32 encoding
|
|
21
|
+
let hrp = bech32::Hrp::parse("npub").unwrap();
|
|
22
|
+
bech32::encode::<bech32::Bech32m>(hrp, &bytes)
|
|
23
|
+
.map_err(|e| PyValueError::new_err(format!("Bech32 encode failed: {}", e)))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Convert bech32 npub to hex format
|
|
27
|
+
#[pyfunction]
|
|
28
|
+
fn npub_to_hex(npub: &str) -> PyResult<String> {
|
|
29
|
+
let (hrp, bytes) = bech32::decode(npub)
|
|
30
|
+
.map_err(|e| PyValueError::new_err(format!("Invalid bech32: {}", e)))?;
|
|
31
|
+
|
|
32
|
+
if hrp.to_string() != "npub" {
|
|
33
|
+
return Err(PyValueError::new_err(format!("Expected npub prefix, got: {}", hrp)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Ok(hex::encode(bytes))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Verify a Groth16 proof (placeholder - full verification requires arkworks)
|
|
40
|
+
#[pyfunction]
|
|
41
|
+
fn verify_proof(py: Python<'_>, proof_json: &str, public_inputs_json: &str, _vk_json: &str) -> PyResult<PyObject> {
|
|
42
|
+
// Parse the proof and public inputs
|
|
43
|
+
let _proof: serde_json::Value = serde_json::from_str(proof_json)
|
|
44
|
+
.map_err(|e| PyValueError::new_err(format!("Invalid proof JSON: {}", e)))?;
|
|
45
|
+
|
|
46
|
+
let public_inputs: Vec<String> = serde_json::from_str(public_inputs_json)
|
|
47
|
+
.map_err(|e| PyValueError::new_err(format!("Invalid public inputs JSON: {}", e)))?;
|
|
48
|
+
|
|
49
|
+
if public_inputs.len() < 3 {
|
|
50
|
+
return Err(PyValueError::new_err("Public inputs must have at least 3 elements"));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Extract npub from public inputs (first element is x-coordinate)
|
|
54
|
+
let npub_hex = format!("{:0>64}", public_inputs[0].trim_start_matches("0x"));
|
|
55
|
+
let npub = hex_to_npub(&npub_hex[..64.min(npub_hex.len())])?;
|
|
56
|
+
|
|
57
|
+
// Build result dict
|
|
58
|
+
let dict = PyDict::new(py);
|
|
59
|
+
dict.set_item("valid", true)?; // Note: actual verification requires arkworks
|
|
60
|
+
dict.set_item("npub", npub)?;
|
|
61
|
+
dict.set_item("npub_hex", &npub_hex)?;
|
|
62
|
+
dict.set_item("merkle_root", &public_inputs[2])?;
|
|
63
|
+
if public_inputs.len() > 3 {
|
|
64
|
+
dict.set_item("session_binding", &public_inputs[3])?;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Ok(dict.into())
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Parse OIDC id_token claims (JWT decode without verification)
|
|
71
|
+
#[pyfunction]
|
|
72
|
+
fn decode_token_claims(py: Python<'_>, token: &str) -> PyResult<PyObject> {
|
|
73
|
+
// Split JWT
|
|
74
|
+
let parts: Vec<&str> = token.split('.').collect();
|
|
75
|
+
if parts.len() != 3 {
|
|
76
|
+
return Err(PyValueError::new_err("Invalid JWT format"));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Decode payload (middle part)
|
|
80
|
+
let payload = base64::Engine::decode(
|
|
81
|
+
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
|
|
82
|
+
parts[1]
|
|
83
|
+
).map_err(|e| PyValueError::new_err(format!("Base64 decode failed: {}", e)))?;
|
|
84
|
+
|
|
85
|
+
let claims: serde_json::Value = serde_json::from_slice(&payload)
|
|
86
|
+
.map_err(|e| PyValueError::new_err(format!("JSON parse failed: {}", e)))?;
|
|
87
|
+
|
|
88
|
+
// Convert to Python dict
|
|
89
|
+
let dict = PyDict::new(py);
|
|
90
|
+
if let serde_json::Value::Object(map) = claims {
|
|
91
|
+
for (k, v) in map {
|
|
92
|
+
match v {
|
|
93
|
+
serde_json::Value::String(s) => { dict.set_item(&k, s)?; }
|
|
94
|
+
serde_json::Value::Number(n) => {
|
|
95
|
+
if let Some(i) = n.as_i64() {
|
|
96
|
+
dict.set_item(&k, i)?;
|
|
97
|
+
} else if let Some(f) = n.as_f64() {
|
|
98
|
+
dict.set_item(&k, f)?;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
serde_json::Value::Bool(b) => { dict.set_item(&k, b)?; }
|
|
102
|
+
serde_json::Value::Null => { dict.set_item(&k, py.None())?; }
|
|
103
|
+
_ => { dict.set_item(&k, v.to_string())?; }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Ok(dict.into())
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// SignedByMe SDK for Python
|
|
112
|
+
///
|
|
113
|
+
/// Example:
|
|
114
|
+
/// from signedby import hex_to_npub, verify_proof
|
|
115
|
+
///
|
|
116
|
+
/// npub = hex_to_npub("0123456789abcdef" * 4)
|
|
117
|
+
/// result = verify_proof(proof_json, public_inputs_json, vk_json)
|
|
118
|
+
#[pymodule]
|
|
119
|
+
fn signedby(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
120
|
+
m.add_function(wrap_pyfunction!(hex_to_npub, m)?)?;
|
|
121
|
+
m.add_function(wrap_pyfunction!(npub_to_hex, m)?)?;
|
|
122
|
+
m.add_function(wrap_pyfunction!(verify_proof, m)?)?;
|
|
123
|
+
m.add_function(wrap_pyfunction!(decode_token_claims, m)?)?;
|
|
124
|
+
Ok(())
|
|
125
|
+
}
|