Skip to content
bytehubioPublic
forked from vzsg/ed25519

About

Ed25519 for Swift 3.x

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

33 Commits

Folders and files

Repository files navigation

Ed25519 for Swift

This project is a Swift adaptation of the portable C Ed25519 implementation available here.
The complete functionality, including the key exchange and scalar addition functions are available.

On both macOS and Linux systems, /dev/urandom will be used for generating seeds.

Installation

To integrate the library in your SwiftPM project, add the following dependency to Package.swift:

.package(url: "https://github.com/bytehubio/ed25519.git", branch: "master")

API

let seed = try Seed()

Creates a 32 byte random seed for key generation.
May throw Ed25519Error.seedGenerationFailed in case there was a problem with reading from /dev/urandom.

let seedBytes: [UInt8] = [ ... ]
let seed2 = try Seed(bytes: seedBytes)

Creates a seed for key generation with a previously known value.
seedBytes must be an array of 32 bytes, otherwise Ed25519Error.invalidSeedLength will be thrown.

let keyPair = KeyPair(seed: seed)

Creates a new key pair from the given seed.

let pubBytes: [UInt8] = [ ... ]
let privBytes: [UInt8] = [ ... ]
let keyPair2 = try KeyPair(publicKey: pubBytes, privateKey: privBytes)

Creates a key pair from the previously known public and private keys.
Throws Ed25519Error.invalidPublicKeyLength if the public key is not 32, or Ed25519Error.invalidPrivateKeyLength if the private key is not 64 bytes long.

let publicKey = try PublicKey(pubBytes)
let privateKey = try PrivateKey(privBytes)
let keyPair3 = KeyPair(publicKey: publicKey, privateKey: privateKey)

Construct public and private keys directly.
The same length limitations and exceptions apply.

publicKey.bytes
privateKey.bytes
keyPair.publicKey.bytes
keyPair.privateKey.bytes
seed.bytes

Access raw byte arrays behind any key or seed with the bytes property.

let message: [UInt8] = [ ... ]
let signature: [UInt8] = keyPair.sign(message)

Creates a signature of the given message with the given key pair.
signature will be an array of 64 bytes.

let valid: Bool = try keyPair.verify(signature: signature, message: message)
let valid2: Bool = try publicKey.verify(signature: signature, message: message)

Verifies the signature on the given message using a key pair or a public key.
Both methods throw Ed25519Error.invalidSignatureLength if signature is not an array of 64 bytes.

let scalar: [UInt8] = [ ... ]
let keyPairMod = try keyPair.add(scalar: scalar)
let publicKeyMod = try publicKey.add(scalar: scalar)
let privateKeyMod = try privateKey.add(scalar: scalar)

Adds scalar to the given key pair, public or private key, where scalar is a 32 byte buffer (possibly generated with a seed), generating new key(s). This is useful for enforcing randomness on a key pair by a third party while only knowing the public key, among other things. Warning: the last bit of the scalar is ignored - if comparing scalars make sure to ignore scalar[31].

let sharedSecret = keyPair.keyExchange()
let sharedSecret2 = try KeyPair.keyExchange(publicKey: pubBytes, privateKey: privBytes)
let sharedSecret3 = KeyPair.keyExchange(publicKey: publicKey, privateKey: privateKey)

Performs a key exchange on the given public key and private key, producing a shared secret, an array of 32 bytes. It is recommended to hash the shared secret before using it.

The public key must be the peer's original Ed25519 public key. The private key must be your own 64-byte expanded secret, as returned by KeyPair(seed:). keyExchange converts the Edwards coordinate internally and clamps the first 32 bytes of the expanded secret; it does not hash the private key again. The C function reads only those first 32 prepared scalar bytes. An expanded secret is neither a 32-byte seed nor libsodium's seed || publicKey secret-key format. These existing key-exchange contracts are unchanged.

Ed25519 public key to X25519 coordinate

import Ed25519

let edwardsPublicKey = try PublicKey(pubBytes)
let montgomeryBytes: [UInt8] = try edwardsPublicKey.toX25519()
// montgomeryBytes contains exactly 32 little-endian bytes.

toX25519() computes u = (1 + y) / (1 - y) mod (2^255 - 19). It ignores the high bit of the last input byte (the sign of x), so both signs give the same result. All output bytes, including leading, internal, and trailing zeros, are preserved.

This is a coordinate conversion, not full public-key validation. It does not check whether the encoding represents an Edwards point or belongs to the main subgroup. For example, y = 0, 2, and 5 are accepted; libsodium's crypto_sign_ed25519_pk_to_curve25519 has a stricter acceptance contract.

Condition Swift error
Input is not exactly 32 bytes Ed25519Error.invalidPublicKeyLength from PublicKey.init
y >= 2^255 - 19, after ignoring the sign bit Ed25519ConversionError.nonCanonicalPublicKey
y = 1, so the denominator is zero Ed25519ConversionError.undefinedMontgomeryCoordinate

Ed25519ConversionError.unexpectedStatus reports an unexpected C status; the Swift wrapper supplies valid output lengths and pointers itself. Constructing a PublicKey still checks only its length; the additional coordinate checks run when toX25519() is called. The existing Ed25519Error cases remain unchanged, including for exhaustive switches in client code.

Do not pass montgomeryBytes into KeyPair.keyExchange or ed25519_key_exchange: those functions expect Edwards input and would convert it a second time. The new method is for consumers that need the Montgomery coordinate, such as an X25519 API.

C conversion API

#include <ed25519.h>

unsigned char montgomery[32];
ed25519_conversion_result status = ed25519_public_key_to_x25519(
    montgomery, sizeof montgomery, edwards_public_key, edwards_public_key_length);
if (status != ED25519_CONVERSION_SUCCESS) {
    /* Handle the specific status; montgomery has not been modified. */
}

The function takes output and input buffer sizes; both must be exactly 32. It returns ED25519_CONVERSION_INVALID_INPUT_LENGTH, ED25519_CONVERSION_INVALID_OUTPUT_LENGTH, ED25519_CONVERSION_NULL_POINTER, ED25519_CONVERSION_NON_CANONICAL_Y, or ED25519_CONVERSION_UNDEFINED_Y on failure. Checks occur in that order, before writing any output. Invalid lengths and null pointers are rejected before any buffer access. For valid lengths, the caller must supply at least 32 readable input bytes and 32 writable output bytes; the function cannot detect a falsely reported allocation size. In-place conversion and overlapping buffers are supported. The output is unchanged on every error. Only ed25519.h is needed; field arithmetic remains internal to the C target.

Migrating swift-extensions-pack

Once its package dependency includes this API, SEPCrypto.Ed25519 can implement its Data adapter using the Swift product:

import Foundation
import Ed25519

// Inside SEPCrypto.Ed25519:
public static func edwardsToMontgomery(bytesData: Data) throws -> Data {
    Data(try PublicKey(Array(bytesData)).toX25519())
}

Array(bytesData) copies the visible bytes of a Data slice and works when startIndex != 0. The library's Swift API continues to use [UInt8]. Propagate the typed errors, or map them to the consumer's existing error type. The consumer can remove its conversion C-adapter target, its target dependency, and its declarations or calls to internal fe_* functions. Keep its dependency on the Ed25519 Swift product. No private scalar or seed conversion is needed for this public-key operation.

Example

Check the unit tests for usage examples.

The fixed conversion vectors use RFC 8032 section 7.1 for Alice and Bob, the recorded seeds for zero-byte cases, and libsodium 1.0.20's published test and expected output. To reproduce them with libsodium 1.0.20, pass each seed to crypto_sign_ed25519_seed_keypair, then convert its outputs using crypto_sign_ed25519_sk_to_curve25519 and crypto_sign_ed25519_pk_to_curve25519. For the shared secret, call crypto_scalarmult_curve25519 with either party's converted scalar and the other's converted public key. Coordinate-only cases follow directly from the documented formula. Libsodium is not a test dependency.

License

The Swift library is released under the MIT license. See LICENSE for details.

The C implementation is (c) 2015 Orson Peters, licensed under the permissive zlib license. See license.txt in CEd25519 for details.

About

Ed25519 for Swift 3.x

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages