oydid 0.5.4 → 0.5.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 +255 -12
- data/lib/oydid/didcomm.rb +2 -2
- data/lib/oydid/log.rb +194 -42
- data/lib/oydid/vc.rb +4 -2
- data/lib/oydid.rb +499 -184
- metadata +102 -102
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e6e4098a11373a9b648447adbc7f6ec7b89d0efced1bb920080797dfdc5d17de
         | 
| 4 | 
            +
              data.tar.gz: bf33e0ddd9abd26a04d92fa945db9183f87000e246f154aa509c55e9b5f4aefd
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 6175bb5b1d28443b26573754dd92f97094a7c139d032252c541a73dbd5019028466847b9602b380cd1ca814eeb3e7843576f7a86a77808a5268af62fe89dfba7
         | 
| 7 | 
            +
              data.tar.gz: a719e0d4c5530340be4ad4a284ffadd46924db95a81c0212fb61e38243cac6a133bf8568ed6407a54d026502048bb06394f5e737f12e0ad588b65c43411bb046
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.5. | 
| 1 | 
            +
            0.5.6
         | 
    
        data/lib/oydid/basic.rb
    CHANGED
    
    | @@ -1,10 +1,16 @@ | |
| 1 1 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            +
            class Hash
         | 
| 5 | 
            +
                def except(*keys)
         | 
| 6 | 
            +
                    self.reject { |key, _| keys.include?(key) }
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 4 10 | 
             
            class Oydid
         | 
| 5 11 |  | 
| 6 12 | 
             
                # basic functions ---------------------------
         | 
| 7 | 
            -
                # %w[multibases multihashes rbnacl json].each { |f| require f }
         | 
| 13 | 
            +
                # %w[multibases multihashes rbnacl json multicodecs].each { |f| require f }
         | 
| 8 14 | 
             
                def self.multi_encode(message, options)
         | 
| 9 15 | 
             
                    method = options[:encode] || DEFAULT_ENCODING rescue DEFAULT_ENCODING
         | 
| 10 16 | 
             
                    case method
         | 
| @@ -54,7 +60,11 @@ class Oydid | |
| 54 60 | 
             
                end
         | 
| 55 61 |  | 
| 56 62 | 
             
                def self.get_digest(message)
         | 
| 57 | 
            -
                     | 
| 63 | 
            +
                    decoded_message, error = Oydid.multi_decode(message)
         | 
| 64 | 
            +
                    if decoded_message.nil?
         | 
| 65 | 
            +
                        return [nil, error]
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
                    retVal = Multihashes.decode decoded_message
         | 
| 58 68 | 
             
                    if retVal[:hash_function].to_s != ""
         | 
| 59 69 | 
             
                        return [retVal[:hash_function].to_s, ""]
         | 
| 60 70 | 
             
                    end
         | 
| @@ -94,7 +104,7 @@ class Oydid | |
| 94 104 | 
             
                end
         | 
| 95 105 |  | 
| 96 106 | 
             
                # key management ----------------------------
         | 
| 97 | 
            -
                def self.generate_private_key(input, method = "ed25519-priv", options)
         | 
| 107 | 
            +
                def self.generate_private_key(input, method = "ed25519-priv", options = {})
         | 
| 98 108 | 
             
                    begin
         | 
| 99 109 | 
             
                        omc = Multicodecs[method].code
         | 
| 100 110 | 
             
                    rescue
         | 
| @@ -104,15 +114,18 @@ class Oydid | |
| 104 114 | 
             
                    case Multicodecs[method].name 
         | 
| 105 115 | 
             
                    when 'ed25519-priv'
         | 
| 106 116 | 
             
                        if input != ""
         | 
| 107 | 
            -
                            raw_key = Ed25519::SigningKey.new(RbNaCl::Hash.sha256(input)) | 
| 117 | 
            +
                            raw_key = Ed25519::SigningKey.new(RbNaCl::Hash.sha256(input))
         | 
| 108 118 | 
             
                        else
         | 
| 109 | 
            -
                            raw_key = Ed25519::SigningKey.generate | 
| 119 | 
            +
                            raw_key = Ed25519::SigningKey.generate
         | 
| 110 120 | 
             
                        end
         | 
| 121 | 
            +
                        raw_key = raw_key.to_bytes
         | 
| 122 | 
            +
                        length = raw_key.bytesize
         | 
| 111 123 | 
             
                    else
         | 
| 112 124 | 
             
                        return [nil, "unsupported key codec"]
         | 
| 113 125 | 
             
                    end
         | 
| 114 | 
            -
                     | 
| 126 | 
            +
                    
         | 
| 115 127 | 
             
                    encoded = multi_encode([omc, length, raw_key].pack("SCa#{length}"), options)
         | 
| 128 | 
            +
                    # encoded = multi_encode(raw_key.to_bytes, options)
         | 
| 116 129 | 
             
                    if encoded.first.nil?
         | 
| 117 130 | 
             
                        return [nil, encoded.last]
         | 
| 118 131 | 
             
                    else
         | 
| @@ -120,7 +133,7 @@ class Oydid | |
| 120 133 | 
             
                    end
         | 
| 121 134 | 
             
                end
         | 
| 122 135 |  | 
| 123 | 
            -
                def self.public_key(private_key, options, method = "ed25519-pub")
         | 
| 136 | 
            +
                def self.public_key(private_key, options = {}, method = "ed25519-pub")
         | 
| 124 137 | 
             
                    code, length, digest = multi_decode(private_key).first.unpack('SCa*')
         | 
| 125 138 | 
             
                    case Multicodecs[code].name
         | 
| 126 139 | 
             
                    when 'ed25519-priv'
         | 
| @@ -132,6 +145,7 @@ class Oydid | |
| 132 145 | 
             
                        else
         | 
| 133 146 | 
             
                            return [nil, "unsupported key codec"]
         | 
| 134 147 | 
             
                        end
         | 
| 148 | 
            +
                        # encoded = multi_encode(public_key.to_bytes, options)
         | 
| 135 149 | 
             
                        length = public_key.to_bytes.bytesize
         | 
| 136 150 | 
             
                        encoded = multi_encode([Multicodecs[method].code, length, public_key].pack("CCa#{length}"), options)
         | 
| 137 151 | 
             
                        if encoded.first.nil?
         | 
| @@ -144,6 +158,29 @@ class Oydid | |
| 144 158 | 
             
                    end
         | 
| 145 159 | 
             
                end
         | 
| 146 160 |  | 
| 161 | 
            +
                def self.getPrivateKey(enc, pwd, dsk, dfl, options)
         | 
| 162 | 
            +
                    if enc.to_s == "" # usually read from options[:doc_enc]
         | 
| 163 | 
            +
                        if pwd.to_s == "" # usually read from options[:doc_pwd]
         | 
| 164 | 
            +
                            if dsk.to_s == "" # usually read from options[:doc_key]
         | 
| 165 | 
            +
                                if dfl.to_s == "" # default file name for key
         | 
| 166 | 
            +
                                    return [nil, "no reference"]
         | 
| 167 | 
            +
                                else
         | 
| 168 | 
            +
                                    privateKey, msg = read_private_key(dfl.to_s, options)
         | 
| 169 | 
            +
                                end
         | 
| 170 | 
            +
                            else
         | 
| 171 | 
            +
                                privateKey, msg = read_private_key(dsk.to_s, options)
         | 
| 172 | 
            +
                            end
         | 
| 173 | 
            +
                        else
         | 
| 174 | 
            +
                            privateKey, msg = generate_private_key(pwd, 'ed25519-priv', options)
         | 
| 175 | 
            +
                        end
         | 
| 176 | 
            +
                    else
         | 
| 177 | 
            +
                        privateKey, msg = decode_private_key(enc.to_s, options)
         | 
| 178 | 
            +
                    end
         | 
| 179 | 
            +
                    return [privateKey, msg]
         | 
| 180 | 
            +
                end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                # if the identifier is already the public key there is no validation if it is a valid key
         | 
| 183 | 
            +
                # (this is a privacy-preserving feature)
         | 
| 147 184 | 
             
                def self.getPubKeyFromDID(did)
         | 
| 148 185 | 
             
                    identifier = did.split(LOCATION_PREFIX).first.split(CGI.escape LOCATION_PREFIX).first rescue did
         | 
| 149 186 | 
             
                    identifier = identifier.delete_prefix("did:oyd:")
         | 
| @@ -166,6 +203,52 @@ class Oydid | |
| 166 203 | 
             
                    end
         | 
| 167 204 | 
             
                end
         | 
| 168 205 |  | 
| 206 | 
            +
                # available key_types
         | 
| 207 | 
            +
                # * doc - document key
         | 
| 208 | 
            +
                # * rev - revocation key
         | 
| 209 | 
            +
                def self.getDelegatedPubKeysFromDID(did, key_type = "doc")
         | 
| 210 | 
            +
                    # retrieve DID
         | 
| 211 | 
            +
                    did_document, msg = read(did, {})
         | 
| 212 | 
            +
                    keys, msg = getDelegatedPubKeysFromFullDidDocument(did_document, key_type)
         | 
| 213 | 
            +
                    if keys.nil?
         | 
| 214 | 
            +
                        return [nil, msg]
         | 
| 215 | 
            +
                    else
         | 
| 216 | 
            +
                        return [keys, ""]
         | 
| 217 | 
            +
                    end
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                def self.getDelegatedPubKeysFromFullDidDocument(did_document, key_type = "doc")
         | 
| 221 | 
            +
                    # get current public key
         | 
| 222 | 
            +
                    case key_type
         | 
| 223 | 
            +
                    when "doc"
         | 
| 224 | 
            +
                        keys = [did_document["doc"]["key"].split(":").first] rescue nil
         | 
| 225 | 
            +
                    when "rev"
         | 
| 226 | 
            +
                        keys = [did_document["doc"]["key"].split(":").last] rescue nil
         | 
| 227 | 
            +
                    else
         | 
| 228 | 
            +
                        return [nil, "invalid key type: " + key_type]
         | 
| 229 | 
            +
                    end
         | 
| 230 | 
            +
                    if keys.nil?
         | 
| 231 | 
            +
                        return [nil, "cannot retrieve current key"]
         | 
| 232 | 
            +
                    end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                    # travers through log and get active delegation public keys
         | 
| 235 | 
            +
                    log = did_document["log"]
         | 
| 236 | 
            +
                    log.each do |item|
         | 
| 237 | 
            +
                        if item["op"] == 5 # DELEGATE
         | 
| 238 | 
            +
                            # !!!OPEN: check if log entry is confirmed / referenced in a termination entry
         | 
| 239 | 
            +
                            item_keys = item["doc"]
         | 
| 240 | 
            +
                            if key_type == "doc" && item_keys[0..3] == "doc:"
         | 
| 241 | 
            +
                                keys << item_keys[4-item_keys.length..]
         | 
| 242 | 
            +
                            elsif key_type == "rev" && item_keys[0..3] == "rev:"
         | 
| 243 | 
            +
                                keys << item_keys[4-item_keys.length..]
         | 
| 244 | 
            +
                            end
         | 
| 245 | 
            +
                        end
         | 
| 246 | 
            +
                    end unless log.nil?
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                    # return array
         | 
| 249 | 
            +
                    return [keys.uniq, ""]
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 169 252 | 
             
                def self.sign(message, private_key, options)
         | 
| 170 253 | 
             
                    code, length, digest = multi_decode(private_key).first.unpack('SCa*')
         | 
| 171 254 | 
             
                    case Multicodecs[code].name
         | 
| @@ -203,7 +286,7 @@ class Oydid | |
| 203 286 | 
             
                    end
         | 
| 204 287 | 
             
                end
         | 
| 205 288 |  | 
| 206 | 
            -
                def self.encrypt(message, public_key, options)
         | 
| 289 | 
            +
                def self.encrypt(message, public_key, options = {})
         | 
| 207 290 | 
             
                    begin
         | 
| 208 291 | 
             
                        code, length, digest = multi_decode(public_key).first.unpack('CCa*')
         | 
| 209 292 | 
             
                        case Multicodecs[code].name
         | 
| @@ -229,7 +312,7 @@ class Oydid | |
| 229 312 | 
             
                    end
         | 
| 230 313 | 
             
                end
         | 
| 231 314 |  | 
| 232 | 
            -
                def self.decrypt(message, private_key, options)
         | 
| 315 | 
            +
                def self.decrypt(message, private_key, options = {})
         | 
| 233 316 | 
             
                    begin
         | 
| 234 317 | 
             
                        cipher = [JSON.parse(message)["value"]].pack('H*')
         | 
| 235 318 | 
             
                        nonce = [JSON.parse(message)["nonce"]].pack('H*')
         | 
| @@ -250,10 +333,160 @@ class Oydid | |
| 250 333 | 
             
                    end
         | 
| 251 334 | 
             
                end
         | 
| 252 335 |  | 
| 336 | 
            +
                # key="812b578d2e357270cbbd26a9dd44f93e5c8a3b44462e271348ce8f742dfe08144d06fd1f64d5a15c5b21564695d0dca9d65af322e8f96ef394400fe255d288cf"
         | 
| 337 | 
            +
                def jweh(key)
         | 
| 338 | 
            +
                    pub_key=key[-64..-1]
         | 
| 339 | 
            +
                    prv_key=key[0..-65]
         | 
| 340 | 
            +
                    
         | 
| 341 | 
            +
                    hex_pub=pub_key
         | 
| 342 | 
            +
                    bin_pub=[hex_pub].pack('H*')
         | 
| 343 | 
            +
                    int_pub=RbNaCl::PublicKey.new(bin_pub)
         | 
| 344 | 
            +
                    len_pub=int_pub.to_bytes.bytesize
         | 
| 345 | 
            +
                    enc_pub=Oydid.multi_encode([Multicodecs["x25519-pub"].code,len_pub,int_pub].pack("CCa#{len_pub}"),{}).first
         | 
| 346 | 
            +
             | 
| 347 | 
            +
                    hex_prv=prv_key
         | 
| 348 | 
            +
                    bin_prv=[hex_prv].pack('H*')
         | 
| 349 | 
            +
                    int_prv=RbNaCl::PrivateKey.new(bin_prv)
         | 
| 350 | 
            +
                    len_prv=int_prv.to_bytes.bytesize
         | 
| 351 | 
            +
                    enc_prv=Oydid.multi_encode([Multicodecs["ed25519-priv"].code,len_prv,int_prv].pack("SCa#{len_prv}"),{}).first
         | 
| 352 | 
            +
                    return [enc_pub, enc_prv]
         | 
| 353 | 
            +
                end
         | 
| 354 | 
            +
             | 
| 355 | 
            +
                # public_key=jweh(key).first
         | 
| 356 | 
            +
                # require 'oydid'
         | 
| 357 | 
            +
                # message="hallo"
         | 
| 358 | 
            +
                # public_key="z6Mv4uEoFYJ369NoE9xUzxG5sm8KpPnvHX6YH6GsYFGSQ32J"
         | 
| 359 | 
            +
                # jwe=Oydid.encryptJWE(message, public_key).first
         | 
| 360 | 
            +
                def self.encryptJWE(message, public_key, options = {})
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                    jwe_header = {"enc":"XC20P"}
         | 
| 363 | 
            +
                    recipient_alg = 'ECDH-ES+XC20PKW'
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                    # Content Encryption ---
         | 
| 366 | 
            +
                    # random nonce for XChaCha20-Poly1305: uses a 192-bit nonce (24 bytes)
         | 
| 367 | 
            +
                    cnt_nnc = RbNaCl::Random.random_bytes(RbNaCl::AEAD::XChaCha20Poly1305IETF.nonce_bytes)
         | 
| 368 | 
            +
                    # random key for XChaCha20-Poly1305: uses a 256-bit key (32 bytes)
         | 
| 369 | 
            +
                    cnt_key = RbNaCl::Random.random_bytes(RbNaCl::AEAD::XChaCha20Poly1305IETF.key_bytes)
         | 
| 370 | 
            +
                    # addtional data
         | 
| 371 | 
            +
                    cnt_aad = jwe_header.to_json
         | 
| 372 | 
            +
                    # setup XChaCha20-Poly1305 for Authenticated Encryption with Associated Data (AEAD)
         | 
| 373 | 
            +
                    cnt_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(cnt_key)
         | 
| 374 | 
            +
                    # encrypt
         | 
| 375 | 
            +
                    msg_enc = cnt_aead.encrypt(cnt_nnc, message, cnt_aad)
         | 
| 376 | 
            +
                    cnt_enc = msg_enc[0...-cnt_aead.tag_bytes]
         | 
| 377 | 
            +
                    cnt_tag = msg_enc[-cnt_aead.tag_bytes .. -1]
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                    # Key Encryption ---
         | 
| 380 | 
            +
                    snd_prv = RbNaCl::PrivateKey.generate
         | 
| 381 | 
            +
                    code, length, digest = Oydid.multi_decode(public_key).first.unpack('CCa*')
         | 
| 382 | 
            +
                    buffer = RbNaCl::Util.zeros(RbNaCl::Boxes::Curve25519XSalsa20Poly1305::PublicKey::BYTES)
         | 
| 383 | 
            +
                    RbNaCl::Signatures::Ed25519::VerifyKey.crypto_sign_ed25519_pk_to_curve25519(buffer, digest)
         | 
| 384 | 
            +
                    shared_secret = RbNaCl::GroupElement.new(buffer).mult(snd_prv.to_bytes)
         | 
| 385 | 
            +
                    jwe_const = [0, 0, 0, 1] + 
         | 
| 386 | 
            +
                        shared_secret.to_bytes.unpack('C*') + 
         | 
| 387 | 
            +
                        [0,0,0,15] + 
         | 
| 388 | 
            +
                        recipient_alg.bytes + 
         | 
| 389 | 
            +
                        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
         | 
| 390 | 
            +
                    kek = RbNaCl::Hash.sha256(jwe_const.pack('C*'))
         | 
| 391 | 
            +
                    snd_nnc = RbNaCl::Random.random_bytes(RbNaCl::AEAD::XChaCha20Poly1305IETF.nonce_bytes)
         | 
| 392 | 
            +
                    snd_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(kek)
         | 
| 393 | 
            +
                    snd_enc = snd_aead.encrypt(snd_nnc, cnt_key, nil)
         | 
| 394 | 
            +
                    snd_key = snd_enc[0...-snd_aead.tag_bytes]
         | 
| 395 | 
            +
                    snd_aut = snd_enc[-snd_aead.tag_bytes .. -1]
         | 
| 396 | 
            +
             | 
| 397 | 
            +
                    # create JWE ---
         | 
| 398 | 
            +
                    jwe_protected = Base64.urlsafe_encode64(jwe_header.to_json).delete("=")
         | 
| 399 | 
            +
                    jwe_encrypted_key = Base64.urlsafe_encode64(snd_key).delete("=")
         | 
| 400 | 
            +
                    jwe_init_vector = Base64.urlsafe_encode64(cnt_nnc).delete("=")
         | 
| 401 | 
            +
                    jwe_cipher_text = Base64.urlsafe_encode64(cnt_enc).delete("=")
         | 
| 402 | 
            +
                    jwe_auth_tag = Base64.urlsafe_encode64(cnt_tag).delete("=")
         | 
| 403 | 
            +
                    rcp_nnc_enc = Base64.urlsafe_encode64(snd_nnc).delete("=")
         | 
| 404 | 
            +
                    rcp_tag_enc = Base64.urlsafe_encode64(snd_aut).delete("=")
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                    jwe_full = {
         | 
| 407 | 
            +
                        protected: jwe_protected,
         | 
| 408 | 
            +
                        iv: jwe_init_vector,
         | 
| 409 | 
            +
                        ciphertext: jwe_cipher_text,
         | 
| 410 | 
            +
                        tag: jwe_auth_tag,
         | 
| 411 | 
            +
                        recipients: [
         | 
| 412 | 
            +
                            {
         | 
| 413 | 
            +
                                encrypted_key: jwe_encrypted_key,
         | 
| 414 | 
            +
                                header: {
         | 
| 415 | 
            +
                                    alg: recipient_alg,
         | 
| 416 | 
            +
                                    iv: rcp_nnc_enc,
         | 
| 417 | 
            +
                                    tag: rcp_tag_enc,
         | 
| 418 | 
            +
                                    epk: {
         | 
| 419 | 
            +
                                        kty: "OKP",
         | 
| 420 | 
            +
                                        crv: "X25519",
         | 
| 421 | 
            +
                                        x: Base64.urlsafe_encode64(snd_prv.public_key.to_bytes).delete("=")
         | 
| 422 | 
            +
                                    }
         | 
| 423 | 
            +
                                }
         | 
| 424 | 
            +
                            }
         | 
| 425 | 
            +
                        ]
         | 
| 426 | 
            +
                    }
         | 
| 427 | 
            +
             | 
| 428 | 
            +
                    jwe = jwe_protected
         | 
| 429 | 
            +
                    jwe += "." + jwe_encrypted_key
         | 
| 430 | 
            +
                    jwe += "." + jwe_init_vector
         | 
| 431 | 
            +
                    jwe += "." + jwe_cipher_text
         | 
| 432 | 
            +
                    jwe += "." + jwe_auth_tag
         | 
| 433 | 
            +
             | 
| 434 | 
            +
                    return [jwe_full, ""]
         | 
| 435 | 
            +
                end
         | 
| 436 | 
            +
             | 
| 437 | 
            +
                # require 'oydid'
         | 
| 438 | 
            +
                # message = '{"protected":"eyJlbmMiOiJYQzIwUCJ9","iv":"G24pK06HSbL0vTlRZMIEBLfa074tJ1tq","ciphertext":"N5bgUr8","tag":"CJeq8cuaercgoBZmrYoGUA","recipients":[{"encrypted_key":"chR8HQh1CRYRU4TdlfBbvon4fcb5PKfWPSo0SgkMC_8","header":{"alg":"ECDH-ES+XC20PKW","iv":"K7lUo8shyJhxC7Nl45VlXes4tbDeZyBL","tag":"2sLCGRv70ESqEAqos3ZhSg","epk":{"kty":"OKP","crv":"X25519","x":"ZpnKcI7Kac6HPwVAGwM0PBweTFKM6wHHVljTHMRWpD4"}}}]}'
         | 
| 439 | 
            +
                # message = jwe.to_json
         | 
| 440 | 
            +
                # private_key = "z1S5USmDosHvi2giCLHCCgcq3Cd31mrhMcUy2fcfszxxLkD7"
         | 
| 441 | 
            +
                # Oydid.decryptJWE(jwe.to_json, private_key)
         | 
| 442 | 
            +
                def self.decryptJWE(message, private_key, options = {})
         | 
| 443 | 
            +
             | 
| 444 | 
            +
                    # JWE parsing
         | 
| 445 | 
            +
                    jwe_full = JSON.parse(message)
         | 
| 446 | 
            +
                    snd_pub_enc = jwe_full["recipients"].first["header"]["epk"]["x"]
         | 
| 447 | 
            +
                    snd_key_enc = jwe_full["recipients"].first["encrypted_key"]
         | 
| 448 | 
            +
                    snd_nnc_enc = jwe_full["recipients"].first["header"]["iv"]
         | 
| 449 | 
            +
                    snd_tag_enc = jwe_full["recipients"].first["header"]["tag"]
         | 
| 450 | 
            +
                    cnt_cip_enc = jwe_full["ciphertext"]
         | 
| 451 | 
            +
                    cnt_tag_enc = jwe_full["tag"]
         | 
| 452 | 
            +
                    cnt_nnc_enc = jwe_full["iv"]
         | 
| 453 | 
            +
                    cnt_aad_enc = jwe_full["protected"]
         | 
| 454 | 
            +
                    recipient_alg = jwe_full["recipients"].first["header"]["alg"]
         | 
| 455 | 
            +
             | 
| 456 | 
            +
                    snd_pub = Base64.urlsafe_decode64(snd_pub_enc)
         | 
| 457 | 
            +
                    snd_nnc = Base64.urlsafe_decode64(snd_nnc_enc)
         | 
| 458 | 
            +
                    snd_key = Base64.urlsafe_decode64(snd_key_enc)
         | 
| 459 | 
            +
                    snd_tag = Base64.urlsafe_decode64(snd_tag_enc)
         | 
| 460 | 
            +
                    cnt_nnc = Base64.urlsafe_decode64(cnt_nnc_enc)
         | 
| 461 | 
            +
                    cnt_cip = Base64.urlsafe_decode64(cnt_cip_enc)
         | 
| 462 | 
            +
                    cnt_tag = Base64.urlsafe_decode64(cnt_tag_enc)
         | 
| 463 | 
            +
                    cnt_aad = Base64.urlsafe_decode64(cnt_aad_enc)
         | 
| 464 | 
            +
             | 
| 465 | 
            +
                    # Key Decryption
         | 
| 466 | 
            +
                    code, length, digest = Oydid.multi_decode(private_key).first.unpack('SCa*')
         | 
| 467 | 
            +
                    buffer = RbNaCl::Util.zeros(RbNaCl::Boxes::Curve25519XSalsa20Poly1305::PublicKey::BYTES)
         | 
| 468 | 
            +
                    RbNaCl::Signatures::Ed25519::SigningKey.crypto_sign_ed25519_sk_to_curve25519(buffer, digest)
         | 
| 469 | 
            +
                    shared_secret = RbNaCl::GroupElement.new(snd_pub).mult(buffer)
         | 
| 470 | 
            +
                    jwe_const = [0, 0, 0, 1] + 
         | 
| 471 | 
            +
                        shared_secret.to_bytes.unpack('C*') + 
         | 
| 472 | 
            +
                        [0,0,0,15] + 
         | 
| 473 | 
            +
                        recipient_alg.bytes + 
         | 
| 474 | 
            +
                        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
         | 
| 475 | 
            +
                    kek = RbNaCl::Hash.sha256(jwe_const.pack('C*'))
         | 
| 476 | 
            +
                    snd_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(kek)
         | 
| 477 | 
            +
                    cnt_key = snd_aead.decrypt(snd_nnc, snd_key+snd_tag, nil)
         | 
| 478 | 
            +
             | 
| 479 | 
            +
                    # Content Decryption
         | 
| 480 | 
            +
                    cnt_aead = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(cnt_key)
         | 
| 481 | 
            +
                    cnt_dec = cnt_aead.decrypt(cnt_nnc, cnt_cip+cnt_tag, cnt_aad)
         | 
| 482 | 
            +
             | 
| 483 | 
            +
                    return [cnt_dec, ""]
         | 
| 484 | 
            +
                end
         | 
| 485 | 
            +
             | 
| 253 486 | 
             
                def self.read_private_key(filename, options)
         | 
| 254 487 | 
             
                    begin
         | 
| 255 488 | 
             
                        f = File.open(filename)
         | 
| 256 | 
            -
                        key_encoded = f.read
         | 
| 489 | 
            +
                        key_encoded = f.read.strip
         | 
| 257 490 | 
             
                        f.close
         | 
| 258 491 | 
             
                    rescue
         | 
| 259 492 | 
             
                        return [nil, "cannot read file"]
         | 
| @@ -284,6 +517,9 @@ class Oydid | |
| 284 517 | 
             
                        when 'ed25519-pub'
         | 
| 285 518 | 
             
                            verify_key = Ed25519::VerifyKey.new(digest)
         | 
| 286 519 | 
             
                            return [verify_key, ""]
         | 
| 520 | 
            +
                        when 'x25519-pub'
         | 
| 521 | 
            +
                            pub_key = RbNaCl::PublicKey.new(digest)
         | 
| 522 | 
            +
                            return [pub_key, ""]
         | 
| 287 523 | 
             
                        else
         | 
| 288 524 | 
             
                            return [nil, "unsupported key codec"]
         | 
| 289 525 | 
             
                        end
         | 
| @@ -331,9 +567,16 @@ class Oydid | |
| 331 567 | 
             
                    case doc_location
         | 
| 332 568 | 
             
                    when /^http/
         | 
| 333 569 | 
             
                        doc_location = doc_location.sub("%3A%2F%2F","://").sub("%3A", ":")
         | 
| 334 | 
            -
                         | 
| 570 | 
            +
                        option_str = ""
         | 
| 571 | 
            +
                        if options[:followAlsoKnownAs]
         | 
| 572 | 
            +
                            option_str = "?followAlsoKnownAs=true"
         | 
| 573 | 
            +
                        end
         | 
| 574 | 
            +
                        retVal = HTTParty.get(doc_location + "/doc/" + doc_identifier + option_str)
         | 
| 335 575 | 
             
                        if retVal.code != 200
         | 
| 336 | 
            -
                            msg = retVal.parsed_response | 
| 576 | 
            +
                            msg = retVal.parsed_response["error"].to_s rescue ""
         | 
| 577 | 
            +
                            if msg.to_s == ""
         | 
| 578 | 
            +
                                msg = "invalid response from " + doc_location.to_s + "/doc/" + doc_identifier.to_s
         | 
| 579 | 
            +
                            end
         | 
| 337 580 | 
             
                            return [nil, msg]
         | 
| 338 581 | 
             
                        end
         | 
| 339 582 | 
             
                        if options.transform_keys(&:to_s)["trace"]
         | 
    
        data/lib/oydid/didcomm.rb
    CHANGED
    
    | @@ -102,12 +102,12 @@ class Oydid | |
| 102 102 | 
             
                # DID Auth for data container with challenge ---
         | 
| 103 103 | 
             
                def self.token_from_challenge(host, pwd, options = {})
         | 
| 104 104 | 
             
                    sid = SecureRandom.hex(20).to_s
         | 
| 105 | 
            +
                    public_key = public_key(generate_private_key(pwd, options).first, options).first
         | 
| 105 106 | 
             
                    retVal = HTTParty.post(host + "/oydid/init",
         | 
| 106 107 | 
             
                                headers: { 'Content-Type' => 'application/json' },
         | 
| 107 | 
            -
                                body: { "session_id": sid }.to_json )
         | 
| 108 | 
            +
                                body: { "session_id": sid, "public_key": public_key }.to_json )
         | 
| 108 109 | 
             
                    challenge = retVal.parsed_response["challenge"]
         | 
| 109 110 | 
             
                    signed_challenge = sign(challenge, Oydid.generate_private_key(pwd, options).first, options).first
         | 
| 110 | 
            -
                    public_key = public_key(generate_private_key(pwd, options).first, options).first
         | 
| 111 111 | 
             
                    retVal = HTTParty.post(host + "/oydid/token",
         | 
| 112 112 | 
             
                                headers: { 'Content-Type' => 'application/json' },
         | 
| 113 113 | 
             
                                body: {
         |