oydid 0.6.5 → 0.6.6
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/oydid/basic.rb +70 -4
- data/spec/oydid_spec.rb +76 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3e47064181aee5338fa3fde2af77f2955cbcb6e5a4b19f8586882183056f1070
|
|
4
|
+
data.tar.gz: d601a5b9151f57af747e1ea42d464549b115cd36eb25cfc42d77385f9c8222a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f40852d750dd966b86b0c7cc7efac6c6a24feac83b9e581284adaa7ebc5231571f790e9c8fccc18c54316b48e7563d9b4e3ec4ee36442184fcafd6e22ac296b6
|
|
7
|
+
data.tar.gz: 0f145b61b4ddd8794316cfc4a5bb653212b1e5be1332dcd24726d30783fbd2448b726cf0cec3a0e2b8f9893d60a8f9605ea591350993ac609f80df6edc262518
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.6.
|
|
1
|
+
0.6.6
|
data/lib/oydid/basic.rb
CHANGED
|
@@ -1012,14 +1012,23 @@ class Oydid
|
|
|
1012
1012
|
end
|
|
1013
1013
|
end
|
|
1014
1014
|
|
|
1015
|
-
#
|
|
1016
|
-
#
|
|
1017
|
-
#
|
|
1018
|
-
def self.
|
|
1015
|
+
# normalize and validate hex input shared by the hex-based converters below:
|
|
1016
|
+
# strips whitespace and an optional '0x' prefix and rejects anything that is
|
|
1017
|
+
# not an even number of hex digits. Returns [normalized_hex, ""] or [nil, msg].
|
|
1018
|
+
def self.normalize_hex(hex)
|
|
1019
1019
|
hex = hex.to_s.strip.delete_prefix("0x").delete_prefix("0X")
|
|
1020
1020
|
unless hex =~ /\A[0-9a-fA-F]+\z/ && hex.length.even?
|
|
1021
1021
|
return [nil, "invalid hex input"]
|
|
1022
1022
|
end
|
|
1023
|
+
return [hex, ""]
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
# build an OYDID multibase-encoded private key from a raw hex-encoded key
|
|
1027
|
+
# (e.g. an externally generated P-256 or Ed25519 key). The key type is taken
|
|
1028
|
+
# from options[:key_type] (default 'ed25519').
|
|
1029
|
+
def self.private_key_from_hex(hex, options = {})
|
|
1030
|
+
hex, msg = normalize_hex(hex)
|
|
1031
|
+
return [nil, msg] if hex.nil?
|
|
1023
1032
|
raw = [hex].pack("H*")
|
|
1024
1033
|
key_type = options[:key_type].to_s
|
|
1025
1034
|
key_type = "ed25519" if key_type == ""
|
|
@@ -1047,6 +1056,63 @@ class Oydid
|
|
|
1047
1056
|
return multi_encode([code, length, raw].pack("SCa#{length}"), options)
|
|
1048
1057
|
end
|
|
1049
1058
|
|
|
1059
|
+
# counterpart to private_key_from_hex for public keys.
|
|
1060
|
+
#
|
|
1061
|
+
# NOTE on the p256 length check: multicodec 0x1200 ('p256-pub') is specified
|
|
1062
|
+
# for compressed points, but Oydid.public_key() emits uncompressed ones
|
|
1063
|
+
# (OpenSSL's default point_conversion_form), so both forms are accepted here
|
|
1064
|
+
# to stay compatible with keys this library produces itself.
|
|
1065
|
+
def self.public_key_from_hex(hex, options = {})
|
|
1066
|
+
hex, msg = normalize_hex(hex)
|
|
1067
|
+
return [nil, msg] if hex.nil?
|
|
1068
|
+
raw = [hex].pack("H*")
|
|
1069
|
+
key_type = options[:key_type].to_s
|
|
1070
|
+
key_type = "ed25519" if key_type == ""
|
|
1071
|
+
case key_type
|
|
1072
|
+
when "p256"
|
|
1073
|
+
unless [33, 65].include?(raw.bytesize)
|
|
1074
|
+
return [nil, "p256 public key must be 33 (compressed) or 65 (uncompressed) bytes"]
|
|
1075
|
+
end
|
|
1076
|
+
# the length alone says nothing: verify that the input actually
|
|
1077
|
+
# decodes to a point on prime256v1 (this also rejects a wrong
|
|
1078
|
+
# leading byte), otherwise an unusable key would be encoded happily
|
|
1079
|
+
begin
|
|
1080
|
+
OpenSSL::PKey::EC::Point.new(
|
|
1081
|
+
OpenSSL::PKey::EC::Group.new("prime256v1"),
|
|
1082
|
+
OpenSSL::BN.new(hex, 16))
|
|
1083
|
+
rescue StandardError
|
|
1084
|
+
return [nil, "p256 public key is not a valid point on the curve"]
|
|
1085
|
+
end
|
|
1086
|
+
code = Multicodecs["p256-pub"].code
|
|
1087
|
+
when "ed25519"
|
|
1088
|
+
unless raw.bytesize == 32
|
|
1089
|
+
return [nil, "ed25519 public key must be 32 bytes (64 hex characters)"]
|
|
1090
|
+
end
|
|
1091
|
+
# there is no cheap point validation for ed25519 (Ed25519::VerifyKey
|
|
1092
|
+
# accepts any 32 bytes), and a private key has the same length - so
|
|
1093
|
+
# passing a *private* key with --public silently yields a public key
|
|
1094
|
+
# multibase. Callers have to know what they hand in.
|
|
1095
|
+
code = Multicodecs["ed25519-pub"].code
|
|
1096
|
+
else
|
|
1097
|
+
return [nil, "unsupported key type"]
|
|
1098
|
+
end
|
|
1099
|
+
return multi_encode(Multibases::DecodedByteArray.new((to_varint(code) << raw.bytes).flatten).to_s(Encoding::BINARY), options)
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1102
|
+
# convert raw hex data (no multicodec prefix, e.g. a signature) to Multibase
|
|
1103
|
+
def self.hex_to_multibase(hex, options = {})
|
|
1104
|
+
hex, msg = normalize_hex(hex)
|
|
1105
|
+
return [nil, msg] if hex.nil?
|
|
1106
|
+
# the multibases gem cannot represent an all-zero byte string: packing
|
|
1107
|
+
# raises NoMethodError and unpacking the correct base58btc form ("z1111")
|
|
1108
|
+
# raises RangeError. Leading zero bytes in otherwise non-zero input are
|
|
1109
|
+
# handled correctly, so only the all-zero case has to be rejected.
|
|
1110
|
+
if hex.delete("0") == ""
|
|
1111
|
+
return [nil, "all-zero input is not supported by the multibase encoder"]
|
|
1112
|
+
end
|
|
1113
|
+
return multi_encode([hex].pack("H*"), options)
|
|
1114
|
+
end
|
|
1115
|
+
|
|
1050
1116
|
# reverse of private_key_from_hex / public_key encoding:
|
|
1051
1117
|
# decode an OYDID Multibase-encoded key (private or public) back to raw hex.
|
|
1052
1118
|
def self.key_to_hex(key_encoded, options = {})
|
data/spec/oydid_spec.rb
CHANGED
|
@@ -238,6 +238,82 @@ describe "OYDID handling" do
|
|
|
238
238
|
expect(Oydid.key_to_hex("not-a-key", {}).first).to be_nil
|
|
239
239
|
end
|
|
240
240
|
|
|
241
|
+
# hex2mb: public keys (--public)
|
|
242
|
+
it "encodes an ed25519 public key from hex identically to public_key()" do
|
|
243
|
+
privkey, _ = Oydid.private_key_from_hex("aa" * 32, {key_type: "ed25519"})
|
|
244
|
+
pub_mb, _ = Oydid.public_key(privkey, {})
|
|
245
|
+
pub_hex, _ = Oydid.key_to_hex(pub_mb, {})
|
|
246
|
+
from_hex, msg = Oydid.public_key_from_hex(pub_hex, {key_type: "ed25519"})
|
|
247
|
+
expect(msg).to eq ""
|
|
248
|
+
expect(from_hex).to eq pub_mb
|
|
249
|
+
expect(Oydid.get_keytype(from_hex)).to eq "ed25519-pub"
|
|
250
|
+
end
|
|
251
|
+
it "encodes a p256 public key from hex identically to public_key()" do
|
|
252
|
+
privkey, _ = Oydid.private_key_from_hex(
|
|
253
|
+
"96fe0f41947d645c7a1858c48c7a0560e7e5bd3d45125b57a611a3a9a103626b",
|
|
254
|
+
{key_type: "p256"})
|
|
255
|
+
pub_mb, _ = Oydid.public_key(privkey, {})
|
|
256
|
+
pub_hex, _ = Oydid.key_to_hex(pub_mb, {})
|
|
257
|
+
from_hex, msg = Oydid.public_key_from_hex(pub_hex, {key_type: "p256"})
|
|
258
|
+
expect(msg).to eq ""
|
|
259
|
+
expect(from_hex).to eq pub_mb
|
|
260
|
+
expect(Oydid.get_keytype(from_hex)).to eq "p256-pub"
|
|
261
|
+
end
|
|
262
|
+
it "round-trips a public key hex -> mb -> hex" do
|
|
263
|
+
hex = "e734ea6c2b6257de72355e472aa05a4c487e6b463c029ed306df2f01b5636b58"
|
|
264
|
+
mb, _ = Oydid.public_key_from_hex(hex, {key_type: "ed25519"})
|
|
265
|
+
back, _ = Oydid.key_to_hex(mb, {})
|
|
266
|
+
expect(back).to eq hex
|
|
267
|
+
end
|
|
268
|
+
it "accepts a compressed p256 public key" do
|
|
269
|
+
hex = "03bcad0c43ac859d0552d95b639156073f9c1c4fb1aa9490f3639a8cf0a2aaadaa"
|
|
270
|
+
mb, msg = Oydid.public_key_from_hex(hex, {key_type: "p256"})
|
|
271
|
+
expect(msg).to eq ""
|
|
272
|
+
expect(Oydid.get_keytype(mb)).to eq "p256-pub"
|
|
273
|
+
end
|
|
274
|
+
it "rejects a p256 public key that is not a point on the curve" do
|
|
275
|
+
mb, msg = Oydid.public_key_from_hex("04" + "11" * 64, {key_type: "p256"})
|
|
276
|
+
expect(mb).to be_nil
|
|
277
|
+
expect(msg).to match(/not a valid point/)
|
|
278
|
+
end
|
|
279
|
+
it "rejects malformed public key hex" do
|
|
280
|
+
expect(Oydid.public_key_from_hex("xyz", {key_type: "ed25519"}).first).to be_nil
|
|
281
|
+
expect(Oydid.public_key_from_hex("aa" * 31, {key_type: "ed25519"}).first).to be_nil
|
|
282
|
+
expect(Oydid.public_key_from_hex("aa" * 32, {key_type: "p256"}).first).to be_nil
|
|
283
|
+
expect(Oydid.public_key_from_hex("aa" * 32, {key_type: "secp256k1"}).first).to be_nil
|
|
284
|
+
end
|
|
285
|
+
it "defaults to ed25519 when no key type is given for a public key" do
|
|
286
|
+
hex = "e734ea6c2b6257de72355e472aa05a4c487e6b463c029ed306df2f01b5636b58"
|
|
287
|
+
expect(Oydid.get_keytype(Oydid.public_key_from_hex(hex, {}).first)).to eq "ed25519-pub"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# hex2mb: raw data (--raw)
|
|
291
|
+
it "encodes raw hex data to multibase without a multicodec prefix" do
|
|
292
|
+
mb, msg = Oydid.hex_to_multibase("deadbeef", {})
|
|
293
|
+
expect(msg).to eq ""
|
|
294
|
+
expect(Oydid.multi_decode(mb).first.unpack1("H*")).to eq "deadbeef"
|
|
295
|
+
# no multicodec prefix -> deliberately not readable back as a key
|
|
296
|
+
expect(Oydid.key_to_hex(mb, {}).first).to be_nil
|
|
297
|
+
end
|
|
298
|
+
it "preserves leading zero bytes in raw hex data" do
|
|
299
|
+
mb, _ = Oydid.hex_to_multibase("00deadbeef", {})
|
|
300
|
+
expect(Oydid.multi_decode(mb).first.unpack1("H*")).to eq "00deadbeef"
|
|
301
|
+
end
|
|
302
|
+
it "accepts a 0x prefix and surrounding whitespace in raw hex data" do
|
|
303
|
+
expect(Oydid.hex_to_multibase(" 0xdeadbeef\n", {}).first).to \
|
|
304
|
+
eq Oydid.hex_to_multibase("deadbeef", {}).first
|
|
305
|
+
end
|
|
306
|
+
it "rejects all-zero raw hex data with a specific message" do
|
|
307
|
+
mb, msg = Oydid.hex_to_multibase("00" * 4, {})
|
|
308
|
+
expect(mb).to be_nil
|
|
309
|
+
expect(msg).to match(/all-zero/)
|
|
310
|
+
end
|
|
311
|
+
it "rejects malformed raw hex data" do
|
|
312
|
+
expect(Oydid.hex_to_multibase("xyz", {}).first).to be_nil
|
|
313
|
+
expect(Oydid.hex_to_multibase("abc", {}).first).to be_nil
|
|
314
|
+
expect(Oydid.hex_to_multibase("", {}).first).to be_nil
|
|
315
|
+
end
|
|
316
|
+
|
|
241
317
|
# storage functions
|
|
242
318
|
it "should create 'filename' and put/read 'text'" do
|
|
243
319
|
@buffer = StringIO.new()
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: oydid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christoph Fabianek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-08-
|
|
10
|
+
date: 2026-08-19 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: simple_dag
|
|
@@ -422,7 +422,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
422
422
|
- !ruby/object:Gem::Version
|
|
423
423
|
version: '0'
|
|
424
424
|
requirements: []
|
|
425
|
-
rubygems_version: 4.0.
|
|
425
|
+
rubygems_version: 4.0.12
|
|
426
426
|
specification_version: 4
|
|
427
427
|
summary: Own Your Decentralized Identifier for Ruby.
|
|
428
428
|
test_files:
|